Codeforces 504E
题意是给一棵树,每个节点上一个字母,m次询问a到b路径的字符串和c到d路径的字符串的lcp.
n = 300000 , m = 1000000 n=300000,m=1000000 n=300000,m=1000000
看上去非常蠢的一个倍增题,一开始我觉得自己想明白了,甚至还质疑了为什么这道题能有3000分。
实际上一个 l o g log log的纯倍增还是漏洞重重的。
其中一个漏洞就是你可以 O ( 1 ) O(1) O(1)得到 v v v往上 2 k 2^k 2k的点,但是你没办法得到 v v v往下 2 k 2^k 2k的点。
不过此时我们得到了一个 l o g 2 log^2 log2的做法,试了一下,光荣TLE.
这个方法有改进的地方,就是得到 v v v往下 2 k 2^k 2k的点可以通过BSGS的思想维护每个点向上 k , 70 k , 4900 k ( 0 < k ≤ 70 ) k,70k,4900k(0<k \le 70) k,70k,4900k(0<k≤70)的点,这样就能 O ( 1 ) O(1) O(1)查询了。 但是还是TLE了
最后改成单哈希7000多ms卡过。
#include<bits/stdc++.h>
#define LL long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
using namespace std;
inline int read(){
int v=0,f=1;
char c=getchar();
while (c<'0' || c>'9'){
if (c=='-') f=-1;
c=getchar();
}
while (c>='0' && c<='9'){
v=v*10+c-'0';
c=getchar();
}
return v*f;
}
const int Maxn=300005;
const LL mod1=1e9+9;
const LL mod2=998244353;
int n;
vector<int> G[Maxn];
char c[Maxn];
LL U1[Maxn][20],D1[Maxn][20];
//LL U2[Maxn][20],D2[Maxn][20];
LL pw1[Maxn];//,pw2[Maxn];
int par[Maxn][20],st[Maxn],ed[Maxn],kc,dep[Maxn];
int up1[Maxn][71],up2[Maxn][71],up3[Maxn][71];
bool ispar(int u,int v){
return u==-1 || (st[u]<=st[v] && ed[v]<=ed[u]);
}
int lca(int u,int v){
if (ispar(u,v)) return u;
for (int i=19;i>=0;i--){
if (!ispar(par[u][i],v)){
u=par[u][i];
}
}
return par[u][0];
}
void dfs(int x,int p){
st[x]=++kc;
par[x][0]=p;
U1[x][0]=D1[x][0]=c[x];
//U2[x][0]=D2[x][0];
for (int i=0;i<G[x].size();i++){
int v=G[x][i];
if (v!=p){
dep[v]=dep[x]+1;
dfs(v,x);
}
}
ed[x]=kc;
}
int lb[Maxn];
int Dv4[Maxn],Dv7[Maxn],Mv4[Maxn],Mv7[Maxn];
int Up(int x,int d){
x=up3[x][Dv4[d]];
d=Mv4[d];
x=up2[x][Dv7[d]];
d=Mv7[d];
x=up1[x][d];
return x;
}
//
int Solve(int x1,int y1,int x2,int y2){
int l1=lca(x1,y1),l2=lca(x2,y2);
//x1->l1 vs x2->l2
int d1=dep[x1]-dep[l1]+1,d2=dep[x2]-dep[l2]+1;
int f1=0,f2=0;
int ans=0,curi=19;
//cout<<d1<<' '<<d2<<endl;
while (1){
if (curi==-1) break;
int cl=1<<curi;
if (cl>d1 || cl>d2){
curi--;
continue;
}
LL H1,H2;
if (!f1){
H1=U1[x1][curi];//*mod2+U2[x1][curi];
}
else{
int rd=d1-cl;
int tmp=Up(y1,rd);
H1=D1[tmp][curi];//*mod2+D2[tmp][curi];
}
if (!f2){
H2=U1[x2][curi];//*mod2+U2[x2][curi];
}
else{
int rd=d2-cl;
int tmp=Up(y2,rd);
H2=D1[tmp][curi];//*mod2+D2[tmp][curi];
}
if (H1!=H2){
curi--;
continue;
}
// cout<<"fuck"<<' '<<curi<<endl;
ans+=cl;
bool ffff=0;
if (!f1){
d1-=cl;
if (d1==0){
f1=1;
d1=dep[y1]-dep[l1];
if (!d1) break;
x1=Up(y1,d1-1);
ffff=1;
}
else{
x1=par[x1][curi];
}
}
else{
d1-=cl;
if (d1==0){
break;
}
x1=Up(y1,d1-1);
}
if (!f2){
d2-=cl;
if (d2==0){
f2=1;
d2=dep[y2]-dep[l2];
if (!d2) break;
x2=Up(y2,d2-1);
ffff=1;
}
else{
// cout<<"FFF"<<' '<<x2<<' '<<curi<<endl;
x2=par[x2][curi];
}
}
else{
d2-=cl;
if (d2==0){
break;
}
x2=Up(y2,d2-1);
}
curi--;
if (ffff) curi=19;
// cout<<x1<<' '<<x2<<' '<<f1<<' '<<f2<<endl;
}
return ans;
}
int main(){
for (int i=0;i<Maxn;i++){
Dv4[i]=i/4900,Mv4[i]=i%4900,Dv7[i]=i/70,Mv7[i]=i%70;
}
scanf("%d",&n);
scanf("%s",c+1);
for (int i=0;i<n-1;i++){
int u,v;
u=read();v=read();
G[u].pb(v);G[v].pb(u);
}
pw1[0]=1;//=pw2[0]=1;
for (int i=1;i<Maxn;i++){
pw1[i]=pw1[i-1]*114514191%mod1;
// pw2[i]=pw2[i-1]*1145141919%mod2;
}
dfs(1,-1);
for (int i=1;i<=n;i++){
up1[i][0]=i;
for (int j=1;j<=70;j++){
if (up1[i][j-1]==-1) up1[i][j]=-1;
else up1[i][j]=par[up1[i][j-1]][0];
}
}
for (int i=1;i<=n;i++){
up2[i][0]=i;
for (int j=1;j<=70;j++){
if (up2[i][j-1]==-1) up2[i][j]=-1;
else up2[i][j]=up1[up2[i][j-1]][70];
}
}
for (int i=1;i<=n;i++){
up3[i][0]=i;
for (int j=1;j<=70;j++){
if (up3[i][j-1]==-1) up3[i][j]=-1;
else up3[i][j]=up2[up3[i][j-1]][70];
}
}
for (int i=1;i<20;i++){
for (int j=1;j<=n;j++){
if (par[j][i-1]==-1){
par[j][i]=-1;
}
else{
int Md=par[j][i-1],Sz=1<<(i-1);
par[j][i]=par[Md][i-1];
U1[j][i]=(U1[j][i-1]*pw1[Sz]+U1[Md][i-1])%mod1;
// U2[j][i]=(U2[j][i-1]*pw2[Sz]+U2[Md][i-1])%mod2;
D1[j][i]=(D1[Md][i-1]*pw1[Sz]+D1[j][i-1])%mod1;
// D2[j][i]=(D2[Md][i-1]*pw2[Sz]+D2[j][i-1])%mod2;
}
}
}
int q;
scanf("%d",&q);
while (q--){
int x1,x2,y1,y2;
x1=read();y1=read();x2=read();y2=read();
printf("%d\n",Solve(x1,y1,x2,y2));
}
return 0;
}
Codeforces 932G
题意:
Given a string s, find the number of ways to split s to substrings such that if there are k substrings (p_1, p_2, p_3, ..., p_k) in partition, then p_i = p_{k - i + 1} for all i (1 ≤ i ≤ k) and k is even.
看了题想了一会,往border方面上想了一会,不会做。
题解的做法非常巧妙。
考虑把字符串按照 s 0 , s n − 1 , s 1 , s n − 2 , . . . s_0,s_{n-1},s_1,s_{n-2},... s0,sn−1,s1,sn−2,...的方式重排成另外一个字符串,那么原来的问题就变成了新的字符串有多少种划分成偶回文串的方法。(这个转化太神仙了,orz)
考虑 d p dp dp,有 f i = ∑ j ∣ s j + 1... i i s p a l i n d r o m e f j f_i=\sum_{j|s_{j+1...i}ispalindrome}f_j fi=∑j∣sj+1...iispalindromefj
暴力做是 O ( n 2 ) O(n^2) O(n2) 的,考虑用回文树来优化这个过程。
我们发现回文树中的border也和普通字符串的border有着相似的性质,一个回文串的回文border其实也是由log个等差数列组成的。这个时候我们维护每一个等差数列,向右推维护 d p dp dp的和即可O(1)求出对于一组border的dp和。
注意不要统计长度为奇数的回文串。
代码懒得写了(有时间再补
题解代码:
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define vi vector<int>
#define all(a) (a).begin(),(a).end()
#define F first
#define S second
#define sz(x) (int)x.size()
#define hell 1000000007
#define endl '\n'
using namespace std;
const int MAXN=1000005;
string s;
struct PalindromicTree{
int N,cur;
vector<map<int,int>> next;
vector<int> link,start,len,diff,slink;
PalindromicTree(): N(0),cur(0){
node();
len[0]=-1;
node();
}
int node(){
next.emplace_back();
link.emplace_back(0);
start.emplace_back(0);
len.emplace_back(0);
diff.emplace_back(0);
slink.emplace_back(0);
return N++;
}
void add_letter(int idx){
while(true){
if(idx-len[cur]-1>=0 && s[idx-len[cur]-1]==s[idx])
break;
cur=link[cur];
}
if(next[cur].find(s[idx])!=next[cur].end()){
cur=next[cur][s[idx]];
return;
}
node();
next[cur][s[idx]]=N-1;
len[N-1]=len[cur]+2;
start[N-1]=idx-len[N-1]+1;
if(len[N-1]==1){
link[N-1]=diff[N-1]=slink[N-1]=1;
cur=N-1;
return;
}
while(true){
cur=link[cur];
if(idx-len[cur]-1>=0 && s[idx-len[cur]-1]==s[idx])
break;
}
link[N-1]=next[cur][s[idx]];
diff[N-1]=len[N-1]-len[link[N-1]];
if(diff[N-1]==diff[link[N-1]])
slink[N-1]=slink[link[N-1]];
else
slink[N-1]=link[N-1];
cur=N-1;
}
};
ll dp[MAXN],sans[MAXN];
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
PalindromicTree pt;
int i,cur;
string str;
cin>>str;
for(i=0;i<sz(str)/2;i++){
s.pb(str[i]);
s.pb(str[sz(str)-i-1]);
}
dp[0]=1;
for(i=1;i<=sz(s);i++){
pt.add_letter(i-1);
for(cur=pt.cur;cur>1;cur=pt.slink[cur]){
sans[cur]=dp[i-pt.len[pt.slink[cur]]-pt.diff[cur]];
if(pt.diff[cur]==pt.diff[pt.link[cur]])
sans[cur]=(sans[cur]+sans[pt.link[cur]])%hell;
dp[i]=(dp[i]+sans[cur])%hell;
}
if(i&1)
dp[i]=0;
}
cout<<dp[sz(s)];
return 0;
}
总时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

5859

被折叠的 条评论
为什么被折叠?



