【Codeforces】547E. Mike and Friends-AC自动机

传送门:cf547E


题解

AC自动机+主席树


代码

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+10;

int n,m,ed[N],fl[N],ch[N][26],df[N],ot[N],dfn,cnt;
int rt[N],ls[N*25],rs[N*25],cot;ll ss[N*25];
string s[N];vector<int>g[N];

inline void ins(int id)
{
	int i,u=0,alp,len;
	cin>>s[id];len=s[id].size();
	for(i=0;i<len;++i){
		alp=s[id][i]-'a';
	    if(!ch[u][alp]) ch[u][alp]=++cnt;
	    u=ch[u][alp];
	}
	ed[id]=u;
}

queue<int>que;
inline void bfs()
{
	int i,u,x,y,z;
	for(i=0;i<26;++i) if(ch[0][i]) que.push(ch[0][i]);
	for(;!que.empty();){
		x=que.front();que.pop();y=fl[x];
		for(i=0;i<26;++i){
			u=ch[y][i];z=ch[x][i];
			if(!z) ch[x][i]=u;
			else{fl[z]=u;que.push(z);}
		}
	}
	for(i=1;i<=cnt;++i) g[fl[i]].pb(i);
}

void ad(int pre,int &k,int l,int r,int pos)
{
	if(k==pre){k=++cot;ls[k]=ls[pre];rs[k]=rs[pre];ss[k]=ss[pre];}
    ss[k]++;if(l^r){
    	if(pos<=mid) ad(ls[pre],ls[k],l,mid,pos);
    	else ad(rs[pre],rs[k],mid+1,r,pos);
    }
}

void dfs(int x)
{df[x]=++dfn;for(int u:g[x]) dfs(u);ot[x]=dfn;}

inline void build(int pre,int &nw,int id)
{
	int i,u=0,len=s[id].size();nw=pre;
	for(i=0;i<len;++i){
		u=ch[u][s[id][i]-'a'];
		if(u) ad(pre,nw,1,dfn,df[u]);
	}
}

ll ask(int pre,int k,int l,int r,int L,int R)
{
	if((!k) || (ss[k]==ss[pre])) return 0;
	if(L<=l && r<=R) return ss[k]-ss[pre];
	if(R<=mid) return ask(ls[pre],ls[k],l,mid,L,R);
	if(L>mid) return ask(rs[pre],rs[k],mid+1,r,L,R);
	return ask(ls[pre],ls[k],l,mid,L,R)+ask(rs[pre],rs[k],mid+1,r,L,R);
}

int main(){
	int i,x,y,z;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i) ins(i);
	bfs();dfs(0);
	for(i=1;i<=n;++i) build(rt[i-1],rt[i],i);
	for(;m;--m){
		scanf("%d%d%d",&x,&y,&z);
		printf("%I64d\n",ask(rt[x-1],rt[y],1,dfn,df[ed[z]],ot[ed[z]]));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这道题是一道博弈论题目,需要使用到 SG 函数。SG 函数是一个函数,它的输入是一个状态,输出是一个非负整数,用来表示当前状态的取胜情况。具体而言,若 SG 函数的输出为 $0$,则当前状态必败;若 SG 函数的输出不为 $0$,则当前状态必胜。 对于这道题目,我们需要先了解一下如何通过数学分析求出 SG 函数。首先,我们需要对游戏的状态进行编码。对于本题,可以将状态表示为一个三元组 $(n, m, k)$,表示当前棋盘的大小为 $n \times m$,每个人每次最多可以取 $k$ 个棋子。接下来,我们需要定义一个从状态到状态集合的映射 $f(\cdot)$,表示从当前状态可以转移到哪些状态。对于本题,$f((n, m, k))$ 中的每个状态可以通过一次操作得到,即将当前棋盘中的某一行或某一列中的 $k$ 个棋子全部取走,然后将其剩余的部分作为新的棋盘状态。注意,对于这个映射,我们只需要考虑下一步能够到达的状态,而不需要考虑更远的状态。 接下来,我们需要定义 SG 函数的递归式。对于一个状态 $(n, m, k)$,其 SG 值可以通过其可以转移到的状态的 SG 值来计算。具体而言,我们可以将当前状态转移到的所有状态的 SG 值进行异或运算,并将结果加上 $1$,即 $SG((n, m, k)) = \text{mex}\{ SG(f((n, m, k))) \} + 1$,其中 $\text{mex}$ 表示集合中未出现的最小非负整数。 最后,我们需要解决的问题就是如何计算 $\text{mex}$ 函数。对于本题,可以使用一个 $O(k)$ 的算法来计算 $\text{mex}$。具体而言,我们可以记录所有出现的 SG 值,然后从 $0$ 开始枚举所有可能的非负整数,找到第一个未出现的整数即为 $\text{mex}$。 下面是 AC 代码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值