GYM103409H Popcount Words 题解

先来三个科技给大家介绍一下:

__builtin_ctz(x)

这表示\(x\)的后缀的\(0\)的个数。

__builtin_clz(x)

这表示\(x\)前缀的\(0\)的个数。(unsigned int 下)

__builtin_parity(x)

这表示\(x\)在二进制下\(1\)的个数的奇偶性。

首先我们容易观察得到,定义\(w_1(l,r)\)表示\(w(l,r)\)反过来的结果,那么\(w(0,2^i-1)=w(0,2^{i-1}-1)+w_1(0,2^{i-1}-1)\),证明考虑前\(2^{i-1}\)个数第\(2^{i-1}\)位为\(0\),后\(2^{i-1}\)个数这些位置为\(1\),其余位置不变,(下文把\(w_0(l,r)\)\(w(l,r)\)视为等价)。这启发我们可以把\(w(l,r)\)拆成\(2\log_2(r-l+1)\)个区间进行运算(相同的高位可以忽略不计),套路的对模式串建出AC自动机,那么我们要求的就是每一个节点及其\(fail\)子树的遍历次数的和,我们注意到我们可以快速计算出\(f_{i,j,k}\)表示第\(i\)个位置在经历\(w(0,2^j-1)_k\)后所变成的结果。然后我们可以定义\(g_{i,j,k}\)表示从第\(i\)个位置发出\(w(0,2^j-1)_k\)的个数,我们可以利用上面的性质进行转移,容易发现\(g_{i,j-1,k}\)\(g_{f_{i,j-1,k},j-1,k\oplus1}\)组成了\(g_{i,j,k}\),我们可以先标记,然后花费\(O(q\log_2V)\)的次数把标记下放到\(g_{i,0,0/1}\),然后就可以轻松处理出每一个点被经过的次数,放到\(fail\)树上跑一下就可以看出来答案了。(实在看不懂的看代码)

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
long long ans[maxn],g[maxn][40][2];
int f[maxn][40][2],ch[maxn][2],fail[maxn],ed[maxn],n,m,x[maxn],y[maxn],l,r,now,tot;
vector<int>tu[maxn];
queue<int>Q;
string s;
int insert(){
	now=0;
	for(int i=0;i<s.size();i++){
		if(!ch[now][s[i]-'0']){
			tot++;
			ch[now][s[i]-'0']=tot;
		}
		now=ch[now][s[i]-'0'];
	}
	return now;
}
void dfs(int q){
	for(int i=0;i<tu[q].size();i++){
		dfs(tu[q][i]);
		ans[q]+=ans[tu[q][i]];
	}
	return;
}
int lowbit(int q){
	return q&(-q);
}
void getfail(){
	for(int i=0;i<2;i++){
		if(ch[0][i]){
			Q.push(ch[0][i]);
		}
	}
	while(!Q.empty()){
		now=Q.front();
		Q.pop();
		tu[fail[now]].push_back(now);
		for(int i=0;i<2;i++){
			if(!ch[now][i]){
				ch[now][i]=ch[fail[now]][i];
			}
			else{
				fail[ch[now][i]]=ch[fail[now]][i];
				Q.push(ch[now][i]);
			}
		}
	}
	return;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>x[i]>>y[i];
	}
	for(int i=1;i<=m;i++){
		cin>>s;
		ed[i]=insert();
	}
	getfail();
	for(int i=0;i<=tot;i++){
		f[i][0][0]=ch[i][0];
		f[i][0][1]=ch[i][1];
	}
	for(int j=1;j<=29;j++){
		for(int i=0;i<=tot;i++){
			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];
		}
	}
	now=0;
	for(int i=1;i<=n;i++){
		l=x[i];
		r=y[i]+1;
		while(l+lowbit(l)<r){
			g[now][__builtin_ctz(l)][__builtin_parity(l)]++;
			now=f[now][__builtin_ctz(l)][__builtin_parity(l)];
			l+=lowbit(l);
		}
		while(l<r){
			g[now][31-__builtin_clz(r-l)][__builtin_parity(l)]++;
			now=f[now][31-__builtin_clz(r-l)][__builtin_parity(l)];
			l+=1u<<(31-__builtin_clz(r-l));
		}
	}
	for(int j=28;j>=0;j--){
		for(int i=0;i<=tot;i++){
			g[i][j][0]+=g[i][j+1][0];
			g[i][j][1]+=g[i][j+1][1];
			g[f[i][j][0]][j][1]+=g[i][j+1][0];
			g[f[i][j][1]][j][0]+=g[i][j+1][1];
		}
	}
	for(int i=0;i<=tot;i++){
		ans[ch[i][0]]+=g[i][0][0];
		ans[ch[i][1]]+=g[i][0][1];
	}
	dfs(0);
	for(int i=1;i<=m;i++){
		cout<<ans[ed[i]]<<'\n';
	}
	return 0;
}
posted @ 2025-05-18 17:26  特别之处  阅读(22)  评论(0)    收藏  举报