gym103409H Popcount Words
数据范围很大啊...考虑对规律进行一个找并拆分成二进制
记\(W_{n,0}=w(0,2^n-1),W_{n,1}=w(2^n,2^{n+1}-1)\)
则对于\(n>0\)有\(W_{n,i}=W_{n-1,i}+W_{n-1,1-i},w_{k2^n,(k+1)2^n-1}=W_{n,popcount(k)\mod 2}\)
第一个式子可以给出一个递推关系,第二个式子可以把一个开头是\(k2^n\)长度为\(2^n\)的串拆下来
那么可以考虑将\(w_{l,r}\)拆分成二进制
具体方法是把每段区间按照左闭右开放到一个长度是\(\infty\)的\(0-base\)线段树上,这样的话就能得到\(O(log)\)个区间满足\(l=k2^n,2^n|(r-l)\).
这样的话就得到了\(n\log V\)个\(W_{n,i}\),考虑如何用它求出答案。
对于询问串建立AC自动机,这样一个串的答案就是它在AC自动机的fail树上的尾节点子树中原串的出现次数。
这个是可以dp的。
设\(f_{u,n,i}\)表示u点经过\(W_{n,i}\)的终点,则对于\(n>0\),\(f_{u,n,i}=f_{f_{u,n-1,i},n-1,1-i}\)
但是我们要求的是出现次数而不是最终节点,所以转移时附带一个\(c_{u,n,i}\)表示直接使用\(W_{n,i}\)在\(u\)转移的次数,
然后设\(g_{u,n,i}\)为\(W_{n,i}\)直接加上间接在\(u\)点转移的次数则有:
\[g_{u,n,i}=c_{u,n,i}+g_{u,n+1,i}+\sum\limits_{f_{v,n+1,1-i}=u}g_{v,n+1,1-i}
\]
那么对于一个点可以把它的\(g_{u,0,0/1}\)分别贡献给它的两个儿子表示它们的被访问次数.然后转移就完事了.
纸上谈兵没卵用,滚去写代码了
O3虽好,可不要贪杯哦
#pragma GCC optimize(3)
#include<vector>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
typedef long long ll;typedef double db;
#define pf printf
#define F(i,a,b) for(int i=a;i<=b;i++)
#define D(i,a,b) for(int i=a;i>=b;i--)
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
const int N=5e5+10;
int son[N][2],tot=1,L[N],R[N],fail[N],w[N][2],cnt,n,m,end[N],f[N][31][2],c[N][31][2];ll sum[N],now[N][2],nxt[N][2];char s[N];
inline int insert(){
int n=strlen(s+1),now=1;
F(i,1,n){
int v=s[i]-'0';
if(!son[now][v])son[now][v]=++tot;
now=son[now][v];
}return now;
}
int q[N],hd=1,tl;
inline void build(){
F(i,0,1)son[0][i]=1;
q[++tl]=1;
while(hd<=tl){
int x=q[hd++];
F(i,0,1){
int v=son[x][i];
if(!v){son[x][i]=son[fail[x]][i];continue;}
fail[v]=son[fail[x]][i];q[++tl]=v;
}
}
}
inline void build(int l,int r,int ql,int qr){
if(ql>=r||l>=qr)return;
if(l>=ql&&r<=qr){
w[++cnt][0]=__builtin_ctz(r-l);
w[cnt][1]=__builtin_popcount(l>>w[cnt][0])&1;
return;
}
int mid=(l+r)>>1;
build(l,mid,ql,qr);build(mid,r,ql,qr);
}
inline short main(){
n=read(),m=read();
F(i,1,n)L[i]=read(),R[i]=read();
F(i,1,m){
scanf("%s",s+1);
end[i]=insert();
}build();
F(i,0,tot)f[i][0][0]=son[i][0],f[i][0][1]=son[i][1];
F(j,1,30)F(i,0,tot)
f[i][j][0]=f[f[i][j-1][0]][j-1][1],
f[i][j][1]=f[f[i][j-1][1]][j-1][0];
int Wow=1;
F(i,1,n){
cnt=0;
build(0,1<<30,L[i],R[i]+1);
F(j,1,cnt){
c[Wow][w[j][0]][w[j][1]]++;
Wow=f[Wow][w[j][0]][w[j][1]];
}
}
D(n,29,0){
memcpy(now,nxt,sizeof(now));
memset(nxt,0,sizeof(nxt));
F(u,1,tot)
nxt[u][0]+=now[u][0]+c[u][n][0],
nxt[u][1]+=now[u][1]+c[u][n][1],
nxt[f[u][n][1]][0]+=now[u][1],
nxt[f[u][n][0]][1]+=now[u][0];
}
F(i,1,tot)sum[son[i][0]]+=nxt[i][0],sum[son[i][1]]+=nxt[i][1];
D(i,tot,1)sum[fail[q[i]]]+=sum[q[i]];
F(i,1,m)pf("%lld\n",sum[end[i]]);
return 0;
}
}
signed main(){return EMT::main();}
Everything that kills me makes me feel alive.