首页 > 其他分享 >Codeforces Gym 103119B - Boring Problem(高斯消元)

Codeforces Gym 103119B - Boring Problem(高斯消元)

时间:2023-05-22 18:34:25浏览次数:61  
标签:int Boring Gym 个数 节点 MAXN MAXP 高斯消

考虑建出 AC 自动机,朴素做法是高斯消元,\(f_i=\sum\limits_{j=0}^{k-1}f_{to_{i,j}}p_j+1\),复杂度 \(O(n^3m^3)\),不能接受。

考虑优化高斯消元的过程,我们定义以下节点为“关键点”:

  • 根节点
  • 对于一个 trie 树(也就是未经过 AC 自动机 getfail 操作得到的树)上有超过两个儿子的节点 \(x\),其所有儿子均为关键点。

我们考虑将所有节点表示为 \(\sum\limits_{j=1}^ccoef_{i,j}f_{pt_j}+coef_{i,c+1}\) 的形式,其中 \(c\) 为关键点个数,\(pt_j\) 为第 \(j\) 个关键点。具体方法是,对于一个在原 trie 树上有恰好一个儿子的儿子的节点 \(i\),其会有恰好一个儿子在 trie 树上深度比其大,记其为 \(son\),我们将含 \(f_{son}\) 的项移到一侧,其他项移到另一侧,这样就由 \(coef_{i}\) 转移得到 \(coef_{son}\)。直接在原树上 bfs 进行这个操作即可。

显然这样相当于用树上高斯消元的技巧手动消掉了所有恰好有一个儿子的点的项,这样继续对那些有 \(\ge 2\) 或 \(0\) 个儿子的节点跑三方的高斯消元解出所有关键点的 \(f\) 即可。显然,我们添加一个字符串时,最多只会多出一个儿子个数 \(\ge 2\) 的点,而且显然关键点个数的增量等于儿子个数 \(\ge 2\) 的点的增量与叶子节点个数的增量之和,也就是说变量个数等于方程个数且不超过 \(2n\),所以时间复杂度是可以接受的。

const int MAXN=200;
const int MAXP=1e4;
const int MOD=1e9+7;
const int INV100=570000004;
int qpow(int x,int e){int ret=1;for(;e;e>>=1,x=1ll*x*x%MOD)if(e&1)ret=1ll*ret*x%MOD;return ret;}
int n,m,k,p[28];string s[MAXN+5];char buf[MAXP+5];
int f[MAXP+5][MAXN+5],g[MAXN+5][MAXN+5],h[MAXN+5],res[MAXP+5],cnt;
namespace AC_Automaton{
	int ch[MAXP+5][28],tr[MAXP+5][28],ncnt,ed[MAXP+5],fail[MAXP+5],fa[MAXP+5],id[MAXP+5],idcnt;
	void insert(string ss){
		int cur=0;
		for(int i=0;i<ss.size();i++){
			if(!ch[cur][ss[i]-'a'])ch[cur][ss[i]-'a']=++ncnt,fa[ncnt]=cur;
			cur=ch[cur][ss[i]-'a'];
		}ed[cur]=1;
	}
	void getfail(){
		queue<int>q;
		for(int i=0;i<k;i++)if(ch[0][i])tr[0][i]=ch[0][i],q.push(tr[0][i]);
		while(!q.empty()){
			int x=q.front();q.pop();
			for(int i=0;i<k;i++){
				if(ch[x][i])tr[x][i]=ch[x][i],fail[tr[x][i]]=tr[fail[x]][i],q.push(tr[x][i]);
				else tr[x][i]=tr[fail[x]][i];
			}
		}
	}
	void init(){
		getfail();id[0]=++idcnt;
		for(int i=0;i<=ncnt;i++){
			int son=0;
			for(int j=0;j<k;j++)if(ch[i][j])++son;
			if(son>=2){
				for(int j=0;j<k;j++)if(ch[i][j])
					id[ch[i][j]]=++idcnt;
			}
		}
//		for(int i=0;i<=ncnt;i++)printf("%d%c",fail[i]," \n"[i==ncnt]);
//		for(int i=0;i<=ncnt;i++)printf("%d%c",id[i]," \n"[i==ncnt]);
//		for(int i=0;i<=ncnt;i++)for(int j=0;j<k;j++)printf("%d%c",tr[i][j]," \n"[j==k-1]);
		queue<int>q;q.push(0);
		while(!q.empty()){
			int x=q.front();q.pop();
			if(id[x])f[x][id[x]]=1;
			else{
				for(int i=1;i<=idcnt+1;i++)f[x][i]=f[fa[x]][i];
				int P=0;
				for(int i=0;i<k;i++){
					if(tr[fa[x]][i]!=x){
						for(int j=1;j<=idcnt+1;j++)
							f[x][j]=(f[x][j]+1ll*(MOD-p[i])*f[tr[fa[x]][i]][j])%MOD;
					}else{
						assert(!P);
						P=qpow(p[i],MOD-2);
					}
				}
				f[x][idcnt+1]=(f[x][idcnt+1]+MOD-1)%MOD;
				for(int i=1;i<=idcnt+1;i++)f[x][i]=1ll*f[x][i]*P%MOD;
			}
			for(int i=0;i<k;i++)if(ch[x][i])q.push(ch[x][i]);
		}
//		for(int i=0;i<=ncnt;i++)for(int j=1;j<=idcnt+1;j++)printf("%d%c",f[i][j]," \n"[j==idcnt+1]);
		for(int i=0;i<=ncnt;i++){
			int son=0;
			for(int j=0;j<k;j++)if(ch[i][j])++son;
			if(son>=2){
				++cnt;
				for(int j=1;j<=idcnt+1;j++)g[cnt][j]=f[i][j];
				for(int j=0;j<k;j++)for(int l=1;l<=idcnt+1;l++)
					g[cnt][l]=(g[cnt][l]+1ll*(MOD-p[j])*f[tr[i][j]][l])%MOD;
				g[cnt][idcnt+1]=MOD-g[cnt][idcnt+1];
				g[cnt][idcnt+1]=(g[cnt][idcnt+1]+1)%MOD;
			}else if(ed[i]){
				++cnt;
				for(int j=1;j<=idcnt+1;j++)g[cnt][j]=f[i][j];
				g[cnt][idcnt+1]=MOD-g[cnt][idcnt+1];
			}
		}
	}
}using namespace AC_Automaton;
void Gauss(){
	assert(cnt==idcnt);
	for(int i=1;i<=cnt;i++){
		int t=0;
		for(int j=i;j<=cnt;j++)if(g[j][i])t=j;
		for(int j=i;j<=idcnt+1;j++)swap(g[i][j],g[t][j]);
		int iv=qpow(g[i][i],MOD-2);
		for(int j=i;j<=idcnt+1;j++)g[i][j]=1ll*g[i][j]*iv%MOD;
		for(int j=i+1;j<=cnt;j++){
			for(int k=i+1;k<=idcnt+1;k++)g[j][k]=(g[j][k]+1ll*(MOD-g[j][i])*g[i][k])%MOD;
			g[j][i]=0;
		}
	}
	for(int i=cnt;i;i--){
		h[i]=g[i][idcnt+1];
		for(int j=i+1;j<=idcnt;j++)h[i]=(h[i]+1ll*(MOD-h[j])*g[i][j])%MOD;
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=0;i<k;i++)scanf("%d",&p[i]),p[i]=1ll*p[i]*INV100%MOD;
	for(int i=1;i<=n;i++){
		scanf("%s",buf+1);
		for(int j=1;j<=m;j++)s[i].pb(buf[j]);
		insert(s[i]);
	}
	init();Gauss();
	for(int i=0;i<=ncnt;i++){
		res[i]=f[i][idcnt+1];
		for(int j=1;j<=idcnt;j++)res[i]=(res[i]+1ll*h[j]*f[i][j])%MOD;
	}
	scanf("%s",buf+1);bool flg=0;int cur=0,len=strlen(buf+1);
	for(int i=1;i<=len;i++){
		cur=tr[cur][buf[i]-'a'];flg|=ed[cur];
		if(flg)printf("%d\n",i);
		else printf("%d\n",(i+res[cur])%MOD);
	}
	return 0;
}
/*
4 4 4
10 20 30 40
cabc
abcb
abbb
cccc
ababacabaabcca
*/
/*
1 37 17
7 8 5 9 2 5 5 6 8 7 4 6 7 5 4 8 4
eplpljcadqaoebbgkdabfodaekoepjafgfodb
*/

标签:int,Boring,Gym,个数,节点,MAXN,MAXP,高斯消
From: https://www.cnblogs.com/tzcwk/p/Gym-103119B.html

相关文章

  • GYM104363 2023 ccpc 黑龙江省赛 题解
    比赛链接:https://codeforces.com/gym/104363题解:B//bySkyRainWind#include<bits/stdc++.h>#definemprmake_pair#definedebug()cerr<<"Yoshino\n"#definepiipair<int,int>#definepbpush_backusingnamespacestd;typedeflong......
  • GYM100722C - Ticket to Ride
    首先考虑\(dp_{i,msk}\)表示当前连通了\(msk\)中所有关键点,并且当前连通的非关键点包含\(i\)的最小代价。然后考虑如何转移。我们先用\(Floyd\)预处理所有点对之间的最短路\(dist_{i,j}\)。同时,每次选取的两个用于合并的关键点集合一定没有交集,所以我们可以直接枚举子集......
  • GYM102392 简要题解
    自己下午闲着没事单挑了一下,两小时左右一度rk1,但后继无力了。。。。A.MaxorMin肯定沿着出现过的数操作;然后发现如果a[i]=k,a[j]>k,a[k]<k就会增加一次操作所以维护一下差分序列即可。B.LevelUp两维DP,这个疑似edu出过。要注意的是:需要关于x排个序,不然会漏一些转移。D.......
  • GYM100198G - PL/Cool
    比较毒瘤的一道模拟。首先,我们考虑如何处理define,我们发现,其中不会出现环,并且所有冲突的定义以第一个为准,那么就想到并查集,将\(x\)的父亲定成\(y\)。只不过我们平时的并查集是无向的,这里是有向的,也就是谁是根是重要的。我们先给所有的定义和被定义的变量映射到一个值,然后用......
  • gym 加载/获取 其它模块/库的自定义环境 为什么不需要import自定义的模块/库 只需impo
    site-packages\gymnasium\__init__.py#Hooktoloadpluginsfromentrypointsload_plugin_envs()在这里载入的其它模块/库的自定义环境 Loadmodules(plugins)usingthegymnasiumentrypointsinordertoregisterexternalmodule'senvironmentson``importgymna......
  • GYM103743H Super Gray Pony - 思维 -
    题目链接:https://codeforces.com/gym/103743/problem/H这应该是近期做出来的最难的题之一了……想了一个多小时首先,如何由\(S\)求得$a^{(n)}(S)$?考虑\(S\)的每一位0/1如果第一位是1,那么相当于就知道了剩下的数字在\(rev(a^{(n-1)})\)(即在右侧)中,此时如果第二位为0,......
  • GYM 101147K Touristic Trip
    首先可以看出这是一个条件概率\(P(A/B)=\frac{P(AB)}{P(B)}\),其中\(A\)事件为“满足在\(Z\)城市时寄出第\(Q\)张明信片”,\(B\)事件为“满足得到的明信片序列与给出的明信片序列相同”那只需要求出\(P(AB)\)和\(P(B)\)就能得到最终答案了首先考虑\(B\)事件发现......
  • Codeforces Gym 103439D - LIS Counting(猜结论+状压)
    一道需要一些猜结论技巧的中档题。首先突破口在于排列长度恰好等于不是额外输入的某个数\(k\)而是LDS与LIS的乘积,这显然启示我们去找一些性质。根据dilworth定理,最长反链等于最小链覆盖,故LIS的长度,就是最少需要的递减数列的个数使得每个元素被覆盖至少一次,而每个递减数......
  • 高斯消元学习笔记
    一、前言讲一下高斯-约旦消元法。它适用于处理\(n\)元1次方程组。误差较小并且好写。二、步骤主要用消元的方式求解,就是一列列处理,每一次处理消掉这一列所有其它的未知数。处理第\(i\)列:找到当前这一列的所有系数的绝对值的最大值,确定在第\(x\)行。如果这一列全......
  • GYM104081 部分题解
    比赛链接:https://codeforces.com/gym/104081目前就做了8题,里面还有4个水题……水题:ACEG,模拟题意即可,C和E有一些细节。不想写题解了F首先目标是如何将这9个数分组,由于答案一定存在,考虑随机化,固定\(a_1\inS_1\),然后随机一个\(a_i\inS_1\),异或得到\(S_1\)的另一......