190403省选模拟

版权声明:随意转载哦......但还是请注明出处吧: https://blog.csdn.net/dreaming__ldx/article/details/89008328

战略游戏(game)

假设现在已经钦定了一条被所有人覆盖的路径 ( u , v ) (u,v) ,我们要计算这条路径的贡献。
那么对于一个点 u u ,设对于 u u 的所有儿子,它们的 s i z e size s 1 , s 2 , . . . , s l s_1,s_2,...,s_l ,那么搞一个生成函数 f ( u ) = i = 1 l ( 1 + s i x ) = i = 0 l a i x i f(u)=\prod_{i=1}^l(1+s_ix)=\sum_{i=0}^la_ix^i
我们记 v a l ( u ) = i = 0 k A k i a i val(u)=\sum_{i=0}^kA_k^ia_i
这个时候不难看出如果 u , v u,v 不为祖孙,那么 v a l ( u ) v a l ( v ) val(u)*val(v) 就是这条路径的贡献。
否则假设 u u 是祖先, t t v v 的祖先且为 u u 的儿子,那么令 g ( u ) = f ( u ) 1 + ( n s i z e u x ) 1 + s i z t x = i = 0 l a i x i , v a l ( u ) = i = 0 k A k i a i g(u)=f(u)*\frac{1+(n-size_ux)}{1+siz_tx}=\sum_{i=0}^la'_ix^i,val'(u)=\sum_{i=0}^kA_k^ia'_i ,这样这条路径的贡献就是 v a l ( u ) v a l ( v ) val'(u)*val(v)
然后 f f 可以用分治 f f t fft 算,时间复杂度总和为 n l o g n 2 nlog^2_n ,后面的则要枚举不同 s i z e size 的子树算,由于树上面一个点的不同 s i z e size 的子树数量是 O ( n ) O(\sqrt n) 的,因此总时间复杂度: O ( n l o g n 2 ) + O ( n n ) O(nlog^2_n)+O(n\sqrt n)
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void update(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
const int N=1e5+5;
typedef vector<int> poly;
int lim,tim,pos[N<<2];
inline void init(const int&up){
	lim=1,tim=0;
	while(lim<=up)lim<<=1,++tim;
	for(ri i=1;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
}
inline void ntt(poly&a,const int&type){
	for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
	int a0,a1,mult=(mod-1)/2,typ=type==1?3:(mod+1)/3,w,wn;
	for(ri mid=1;mid<lim;mid<<=1,mult>>=1){
		wn=ksm(typ,mult);
		for(ri j=0,len=mid<<1;j<lim;j+=len){
			w=1;
			for(ri k=0;k<mid;++k,w=mul(w,wn)){
				a0=a[j+k],a1=mul(a[j+k+mid],w);
				a[j+k]=add(a0,a1),a[j+k+mid]=dec(a0,a1);
			}
		}
	}
	if(type==-1)for(ri i=0,inv=ksm(lim,mod-2);i<lim;++i)a[i]=mul(a[i],inv);
}
inline poly operator*(poly a,poly b){
	int n=a.size()-1,m=b.size()-1;
	init(n+m);
	a.resize(lim),b.resize(lim);
	ntt(a,1),ntt(b,1);
	for(ri i=0;i<lim;++i)a[i]=mul(a[i],b[i]);
	return ntt(a,-1),a;
}
int tmp[N],top;
inline poly solve(int l,int r){
	if(l==r){poly ret;return ret.push_back(1),ret.push_back(tmp[l]),ret;}
	int mid=l+r>>1;
	return solve(l,mid)*solve(mid+1,r);
}
vector<int>e[N],coe[N],upd;
int fa[N],sum[N],siz[N],ans=0,n,k,fac[N],ifac[N];
inline void init_inv(){
	fac[0]=fac[1]=ifac[0]=ifac[1]=1;
	for(ri i=2;i<=k;++i)fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[mod-mod/i*i],mod-mod/i);
	for(ri i=2;i<=k;++i)ifac[i]=mul(ifac[i-1],ifac[i]);
}
void dfs1(int p){
	siz[p]=1;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p])continue;
		fa[v]=p,dfs1(v),siz[p]+=siz[v];
		update(ans,mul(sum[p],sum[v])),update(sum[p],sum[v]);
	}
	int val=top=0;
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^fa[p])tmp[++top]=siz[v];
	if(top){
		coe[p]=solve(1,top);
		for(ri i=min(k,top);~i;--i)update(val,mul(ifac[k-i],coe[p][i]));
		update(sum[p],mul(val,fac[k]));		
	}
	else sum[p]=1;
}
map<int,int>mp[N];
typedef map<int,int>::iterator It;
inline void mul(poly&a,const int&b){
	a.push_back(0);
	for(ri i=a.size()-2;~i;--i)update(a[i+1],mul(a[i],b));
}
inline void div(poly&a,const int&b){
	int inv=ksm(b,mod-2);
	poly t=a;
	for(ri i=a.size()-1;i;--i)a[i-1]=mul(t[i],inv),t[i-1]=dec(t[i-1],a[i-1]);
	a.pop_back();
}
void dfs2(int p){
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^fa[p])dfs2(v),update(mp[p][siz[v]],sum[v]);
	int val;
	for(It it=mp[p].begin();it!=mp[p].end();++it){
		val=0;
		upd=coe[p];
		div(upd,it->fi),mul(upd,n-siz[p]);
		for(ri i=min((int)coe[p].size()-1-(p==1),k);~i;--i)update(val,mul(ifac[k-i],upd[i]));
		update(ans,mul(mul(val,fac[k]),it->se));
	}
}
int main(){
	n=read(),k=read();
	if(k==1)cout<<(ll)n*(n-1)/2%mod,exit(0);
	init_inv();
	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	dfs1(1);
	dfs2(1);
	cout<<ans;
	exit(0);
} 

生成膜咒(string)

51nod原题
考虑对于一个串,每个前缀的贡献就是它在里面出现的次数,然后对于每次的答案用增量法算新的后缀的贡献,转化一下就是在 s a m sam f a i l fail 树上面把其对应节点到跟这条路径整体加贡献,离线+树剖搞。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void modify(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
const int N=4e5+5;
int n,m;
char s[N];
namespace Sam{
	int len[N],son[N][26],link[N],tot=1,pos[N],last=1;
	inline void expand(const int&x,const int&id){
		int p=last,np=++tot;
		len[last=pos[id]=np]=len[p]+1;
		while(p&&!son[p][x])son[p][x]=np,p=link[p];
		if(!p){link[np]=1;return;}
		int q=son[p][x],nq;
		if(len[q]==len[p]+1){link[np]=q;return;}
		len[nq=++tot]=len[p]+1,memcpy(son[nq],son[q],sizeof(son[nq])),link[nq]=link[q],link[q]=link[np]=nq;
		while(p&&son[p][x]==q)son[p][x]=nq,p=link[p];
	}
	int num[N],pred[N],dep[N],top[N],fa[N],siz[N],hson[N],Tot=0;
	vector<int>e[N];
	void dfs1(int p){
		siz[p]=1;
		for(ri i=0,v;i<e[p].size();++i){
			fa[v=e[p][i]]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
			if(siz[v]>siz[hson[p]])hson[p]=v;
		}
	}
	void dfs2(int p,int tp){
		top[p]=tp,pred[num[p]=++Tot]=p;
		if(!hson[p])return;
		dfs2(hson[p],tp);
		for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^hson[p])dfs2(v,v);
	}
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (l+r>>1)
	int upd[N<<2],tag[N<<2],sum[N<<2];
	inline void pushup(int p){sum[p]=add(sum[lc],sum[rc]);}
	inline void pushnow(int p,int v){modify(sum[p],mul(upd[p],v)),modify(tag[p],v);}
	inline void pushdown(int p){if(tag[p])pushnow(lc,tag[p]),pushnow(rc,tag[p]),tag[p]=0;}
	inline void build(int p,int l,int r){
		if(l==r){upd[p]=len[pred[l]]-len[link[pred[l]]];return;}
		build(lc,l,mid),build(rc,mid+1,r);
		upd[p]=add(upd[lc],upd[rc]);
	}
	inline int query(int p,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr)return 	tot=sum[p],pushnow(p,1),tot;
		pushdown(p);
		if(qr<=mid)return tot=query(lc,l,mid,ql,qr),pushup(p),tot;
		if(ql>mid)return tot=query(rc,mid+1,r,ql,qr),pushup(p),tot;
		return tot=add(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr)),pushup(p),tot;
	}
	#undef lc
	#undef rc
	#undef mid
	inline int query(int x){
		int ret=0;
		while(x)modify(ret,query(1,1,n,num[top[x]],num[x])),x=fa[top[x]];
		return ret;
	}
	inline void build(){
		for(ri i=1;i<=n;++i)expand(s[i]-'a',i);
		for(ri i=tot;i^1;--i)e[link[i]].push_back(i);
		dfs1(1),dfs2(1,1);
		m=n,n=tot;
		build(1,1,n);
	}
	inline void solve(){
		for(ri ans=0,sum=0,p,i=1;i<=m;++i){
			p=pos[i];
			modify(sum,query(p));
			modify(ans,sum);
			cout<<ans<<'\n';
		}
	}
}
int main(){
	scanf("%d%s",&n,s+1);
	Sam::build();
	Sam::solve();
	return 0;
}

猎人杀(hunter)

这场比赛T2的升级版。
我们发现就是从走 1 1 步变成了走 k k 步。
原题的转移是每一层只有一个环。
现在可能有多个,然而它们是互不相交的,因此可以用 v i s vis 记一下未访问过的。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int mod=1e9+7,inv=5e8+4;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
const int N=2005;
struct dat{
	int x,y;
	dat(int x=0,int y=0):x(x),y(y){}
	friend inline dat operator+(const dat&a,const dat&b){return dat(add(a.x,b.x),add(a.y,b.y));}
	friend inline dat operator+(const dat&a,const int&b){return a+dat(0,b);}
	friend inline dat operator*(const dat&a,const int&b){return dat(mul(a.x,b),mul(a.y,b));}
}g[N];
bool vis[N];
int f[N][N],k,idx[N];
inline int preidx(const int&i,const int&j){return j^k?(j>k?j-k:j-k+i):i;}
inline int sufidx(const int&i,const int&j){return j+k<=i?j+k:j+k-i;}
inline void update(int n,int m){
	vis[m]=1,idx[1]=m;
	if(m^k)g[1]=dat(inv,mul(inv,f[n-1][preidx(n,m)]));
	else g[1]=dat(inv,0);
	int p=sufidx(n,m),pre=m,tim=2,x0;
	while(!vis[p]){
		idx[tim]=p,vis[p]=1;
		if(p^k)g[tim]=(g[tim-1]+f[n-1][pre])*inv;
		else g[tim]=g[tim-1]*inv;
		pre=p,p=sufidx(n,p),++tim;
	}
	--tim;
	x0=mul(g[tim].y,ksm(dec(1,g[tim].x),mod-2));
	for(ri i=1;i<=tim;++i)f[n][idx[i]]=add(mul(g[i].x,x0),g[i].y);
}
int n,K;
int main(){
	cin>>n>>K;
	if(n==1)return cout<<"1",0;
	f[1][1]=1;
	for(ri i=2;i<=n;++i){
		for(ri j=1;j<i;++j)vis[j]=0;
		k=K==K/i*i?i:K-K/i*i;
		for(ri j=1;j<=i;++j)if(!vis[j])update(i,j);
	}
	cout<<f[n][1];
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/89008328