QOJ 6842

传送门

牛牛题。

首先看到一车模式串在一个文本串中的出现次数直接想到建出 AC 自动机。但是文本串实在太大,直接跑是不现实的。

首先注意到 \(w(0,2^n-1)\) 可以拆分成 \(w(0,2^{n-1}-1)+w(2^{n-1},2^{n}-1)\),且后者是前者的反转。

考虑求出 \(g_{i,j,0/1}\) 表示 AC 自动机上从点 \(i\) 经过 \(w(0,2^n-1)/w(2^n,2^{n+1}-1)\) 之后所到达的点,再求出 \(f_{i,j,0/1}\) 则表示跑文本串时这一段扫过的次数。

注意到任意一对 \([l_i,r_i]\) 都可以线段树式地拆分成 \(O(log\ V)\) 个区间,那么将模式串拆分后就可以直接在 AC 自动机上跑。

求出了 \(f_{i,j,0/1}\) 后考虑将其一路下放至 \(f_{i,j-1,0/1}\),直到最后的 \(f_{i,0,0/1}\),它表示了点 \(g_{i,0,0/1}\) 的经过次数。最后一遍 dfs 这题就做完了。

code

#include <bits/stdc++.h>
//taskkill /f /im 未命名1.exe
#define ED cerr<<endl;
#define TS cerr<<"I AK IOI"<<endl;
#define cr(x) cerr<<x<<endl;
#define cr2(x,y) cerr<<x<<" "<<y<<endl;
#define cr3(x,y,z) cerr<<x<<" "<<y<<" "<<z<<endl;
#define cr4(x,y,z,w) cerr<<x<<" "<<y<<" "<<z<<" "<<w<<endl;
#define print(a,l,r) for(int i=l;i<=r;++i) cerr<<a[i]<<' ';puts("");
#define popcnt __builtin_popcount
#define all(s) s.begin(),s.end()
#define bstring basic_string
//#define add(x,y) (x+=y)%=mod
#define pii pair<int,int>
#define epb emplace_back
#define pb push_back
#define mk make_pair
#define ins insert
#define fi first
#define se second
#define ll long long
//#define ull unsigned long long
using namespace std;
const int N=5e5+5,INF=2e9,mod=1e9+7;
int t,n,q;
char s[N];vector<pii> S;vector<int> e[N];
int tr[N][2],ne[N],id[N],idx=0;
ll f[N][31][2],ans[N];int g[N][31][2];

void Add(int l,int r,int L,int R) {
	if(L<=l&&r<=R) {
		S.epb(__lg(r-l+1),popcnt(l)&1);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) Add(l,mid,L,R);
	if(R>mid) Add(mid+1,r,L,R);
}

void insert(char s[],int ID) {
	int u=0;
	for(int i=0;s[i];++i) {
		int to=s[i]-'0';
		if(!tr[u][to]) tr[u][to]=++idx;
		u=tr[u][to];
	}
	id[ID]=u;
}

void build() {
	queue<int> q;
	for(int i=0;i<2;++i) {
		if(tr[0][i]) q.push(tr[0][i]);
	}
	while(!q.empty()) {
		int u=q.front();q.pop();
		for(int i=0;i<2;++i) {
			int c=tr[u][i];
			if(!c) tr[u][i]=tr[ne[u]][i];
			else {
				ne[c]=tr[ne[u]][i];
				q.push(c);
			}
		}
	}
}

void dfs(int u) {
	for(auto to:e[u]) {
		dfs(to),ans[u]+=ans[to];
	}
}

void sol() {
	scanf("%d%d",&n,&q);
	int l,r,all=(1<<30)-1;
	for(int i=1;i<=n;++i) {
		scanf("%d%d",&l,&r),Add(0,all,l,r);
	}
	for(int i=1;i<=q;++i) {
		scanf("%s",s),insert(s,i);
	}
	build();
	for(int i=0;i<=idx;++i) {
		g[i][0][0]=tr[i][0];
		g[i][0][1]=tr[i][1];
		if(i) e[ne[i]].epb(i);
	}
	for(int i=1;i<=30;++i) {
		for(int j=0;j<=idx;++j) {
			g[j][i][0]=g[g[j][i-1][0]][i-1][1];
			g[j][i][1]=g[g[j][i-1][1]][i-1][0];
		}
	}
	int p=0;
	for(auto it:S) {
		int cnt=it.fi,op=it.se;
		++f[p][cnt][op],p=g[p][cnt][op];
	}
	for(int i=30;i>=1;--i) {
		for(int j=0;j<=idx;++j) {
			f[j][i-1][0]+=f[j][i][0];
			int to=g[j][i-1][0];
			f[to][i-1][1]+=f[j][i][0];
			f[j][i-1][1]+=f[j][i][1];
			to=g[j][i-1][1];
			f[to][i-1][0]+=f[j][i][1];
		}
	}
	for(int i=0;i<=idx;++i) {
		ans[tr[i][0]]+=f[i][0][0];
		ans[tr[i][1]]+=f[i][0][1];
	}
	dfs(0);
	for(int i=1;i<=q;++i) {
		printf("%lld\n",ans[id[i]]);
	}
}

int main()
{
	t=1;
	while(t--) {
		sol();
	}
	return 0;
}
posted @ 2025-07-21 20:20  Oier_szc  阅读(8)  评论(0)    收藏  举报