20200705模擬賽 Permutation, LCM game, Easy Data Structure

T1

題目描述:

在這裏插入圖片描述
n5000n\le5000

題目分析:

簽到神仙題。

每條邊的權值相當於是規定了子樹內的點要被劃分成 邊權/2 段。
然後要把兒子的段以及自己合併成當前需要的段數,要保證在同一個兒子子樹內的不能相鄰。
於是容斥,欽定兒子子樹的段至少有 ii 段相鄰,帶上 (1)i(-1)^i 的係數。
然後合併轉移,複雜度 O(n2)O(n^2)
合併完之後再劃分爲當前需要的段數。
根節點確定在第一位,特殊處理,最後答案因爲是環所以乘上 nn

Code:

#include<bits/stdc++.h>
#define maxn 5005
using namespace std;
int n,mod,f[maxn],g[maxn],h[maxn],fac[maxn],inv[maxn],C[maxn][maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],tot;
void line(int x,int y,int z){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,w[tot]=z;}
void dfs(int u,int ff,int K){
	for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff) dfs(v,u,w[i]);
	g[1]=1; int S = 1;
	for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
		for(int j=1;j<=S+w[i];j++) h[j]=0;
		for(int j=1;j<=S;j++)
			for(int k=1;k<=w[i];k++)
				h[j+k]=(h[j+k]+1ll*(w[i]-k&1?-1:1)*g[j]*f[v]%mod*C[w[i]-1][k-1]%mod*inv[k])%mod;
		S+=w[i];
		for(int j=1;j<=S;j++) g[j]=h[j];
	}
	if(u!=1) for(int i=K;i<=S;i++) f[u]=(f[u]+1ll*g[i]*fac[i]%mod*C[i-1][K-1])%mod;
	else for(int i=1;i<=S;i++) f[1]=(f[1]+1ll*g[i]*fac[i-1])%mod;
}
int main()
{
	freopen("permu.in","r",stdin);
	freopen("permu.out","w",stdout);
	scanf("%d%d",&n,&mod);
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=2;i<=n;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;
	for(int i=0;i<=n;i++)
		for(int j=C[i][0]=1;j<=i;j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	for(int i=1,x,y,z;i<n;i++) scanf("%d%d%d",&x,&y,&z),z>>=1,line(x,y,z),line(y,x,z);
	dfs(1,0,0);
	printf("%d\n",(1ll*f[1]*n%mod+mod)%mod);
}

T2

題目描述:

在這裏插入圖片描述
在這裏插入圖片描述

題目分析:

咕咕咕
乘積很好算,不多說。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

T3

題目描述:

在這裏插入圖片描述
在這裏插入圖片描述

題目分析:

一段 typetype212121212121 的串,它的運算結果可以寫成一個 2×22\times 2 的矩陣,表示左邊爲 0/1 時算到右邊爲 0/1 的概率。

如果沒有括號,可以用線段樹維護這個矩陣。

括號實際上形成了一個樹形結構,一棵子樹表示一個括號,根記錄這棵子樹的運算結果爲 0/1 的概率。

重鏈剖分,每個點用根據輕兒子維護出一個矩陣表示重兒子爲 0/1 時子樹結果爲 0/1 的概率,重鏈用線段樹維護。

看上去維護矩陣還需要對輕兒子建線段樹,但是實際上我們可以把這棵樹轉成二叉樹的形式。
(不把運算符看做點是可以做的,但是看做點會方便許多)

因爲這個題的運算符都是二元運算符,所以可以把一個括號內的表達式表示成這種形式:
在這裏插入圖片描述
這樣就形成了一個二叉樹的結構,維護的東西仍然是重兒子爲 0/1 時子樹運算結果爲 0/1 的概率。此時非葉節點都是運算符,葉節點爲 0/1。

實際上不需要維護矩陣,只需要兩個數,表示重兒子爲 0 時結果爲 0 的概率,以及重兒子爲 1 時結果爲 1 的概率。因爲結果要麼是 1 要麼是 0,所以可以推出另外兩個概率。
並且這樣根節點維護的信息就是對應結果爲 0/1 的概率。

然後就是動態DP模板了。

Code:

#include<bits/stdc++.h>
#define maxn 200005
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const int mod = 998244353;
int n,m,ch[maxn][2],root,fa[maxn],siz[maxn],son[maxn],top[maxn],dfn[maxn],ln[maxn],bot[maxn],tim;
int Pow(int a,int b){int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod);return s;}
struct node{
	int t,x,y,z;
	void Div(){
		int s=Pow(x+y+z,mod-2);
		x=1ll*x*s%mod,y=1ll*y*s%mod,z=1ll*z*s%mod;
	}
}A[maxn];
struct data{
	int a,b;//son is 0, to be 0 possibility is a, son is 1, to be 1 possibility is b.
	data operator + (const data &p)const{
		return (data){(1ll*p.a*a+1ll*(1-p.a)*(1-b))%mod,(1ll*(1-p.b)*(1-a)+1ll*p.b*b)%mod};
	}
}g[maxn],t[maxn<<2];
int rt[maxn],lc[maxn<<2],rc[maxn<<2],sz;
void init(int u,const data &p){
	g[u].a=(A[u].x+1ll*(A[u].y+A[u].z)*p.a)%mod;
	g[u].b=(1ll*A[u].x*p.b+1ll*A[u].y*(p.a+p.b)+1ll*A[u].z*p.a)%mod;
}
void build(int &i,int l,int r){
	if(i=++sz,l==r) {t[i]=g[ln[l]];return;}
	int mid=(l+r)>>1;
	build(lc[i],l,mid),build(rc[i],mid+1,r);
	t[i]=t[lc[i]]+t[rc[i]];
}
void dfs1(int u){
	siz[u]=1; if(!ch[u][0]) return;
	for(int i=0,v;i<2;i++) fa[v=ch[u][i]]=u,dfs1(v),siz[u]+=siz[v],siz[v]>siz[son[u]]&&(son[u]=v);
}
void dfs2(int u,int tp){
	top[u]=tp,ln[dfn[u]=++tim]=u;
	if(son[u]){
		dfs2(son[u],tp); int v=ch[u][ch[u][0]==son[u]];
		dfs2(v,v),init(u,t[rt[v]]);
	}
	else bot[tp]=tim,g[u]=(data){A[u].x,A[u].y};
	if(u==tp) build(rt[u],dfn[u],bot[u]);
}
void ins(int i,int l,int r,int x){
	if(l==r) {t[i]=g[ln[x]];return;}
	int mid=(l+r)>>1;
	x<=mid?ins(lc[i],l,mid,x):ins(rc[i],mid+1,r,x);
	t[i]=t[lc[i]]+t[rc[i]];
}
void Modify(int u){
	if(A[u].t==1) g[u]=(data){A[u].x,A[u].y};
	else init(u,t[rt[ch[u][ch[u][0]==son[u]]]]);
	int tp=top[u];
	ins(rt[tp],dfn[tp],bot[tp],dfn[u]);
	for(u=fa[tp];u;u=fa[tp])
		init(u,t[rt[tp]]),tp=top[u],ins(rt[tp],dfn[tp],bot[tp],dfn[u]);
}
int S[maxn],L[maxn],tp,bt;
int Build(int l,int r){
	if(l==r) return S[l];
	ch[S[r-1]][1]=S[r],ch[S[r-1]][0]=Build(l,r-2);
	return S[r-1];
}
int main()
{
	freopen("structure.in","r",stdin);
	freopen("structure.out","w",stdout);
	read(n),read(m);
	for(int i=1;i<=n;i++){
		read(A[i].t);
		if(A[i].t==1) read(A[i].x),read(A[i].y);
		if(A[i].t==2) read(A[i].x),read(A[i].y),read(A[i].z);
		if(A[i].t<=2) S[++tp]=i,A[i].Div();
		if(A[i].t==3) L[++bt]=tp+1;
		if(A[i].t==4) S[L[bt]]=Build(L[bt],tp),tp=L[bt--];
	}
	root=Build(1,tp);
	dfs1(root),dfs2(root,root);
	for(int i;m--;){
		read(i);
		read(A[i].x),read(A[i].y); if(A[i].t==2) read(A[i].z); A[i].Div();
		Modify(i);
		printf("%d\n",(t[rt[root]].b+mod)%mod);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章