#3688. 毒瘤(duliu)

題目描述

從前有一名毒瘤。

毒瘤最近發現了量產毒瘤題的奧祕。考慮如下類型的數據結構題:給出一個數組,要求支持若干種奇奇怪怪的修改操作(例如給一個區間內的數同時加上 cc,或者將一個區間內的數同時開平方根),並且支持詢問區間的和。毒瘤考慮了 nn 個這樣的修改操作,並將它們編號爲 1n1 \ldots n。當毒瘤要出數據結構題的時候,他就將這些修改操作中選若干個出來,然後出成一道題。

當然了,這樣出的題有可能不可做。通過精妙的數學推理,毒瘤揭露了這些修改操作之間的關係:有 mm 對「互相排斥」的修改操作,第 ii 對是第 uiu_i 個操作和第 viv_i 個操作。當一道題中同時含有 uiu_iviv_i 這兩個操作時,這道題就會變得不可做。另一方面,當一道題中不包含任何「互相排斥」的操作時,這個題就是可做的。此外,毒瘤還發現了一個規律:mnm − n 是一個很小的數字(參見「數據範圍」中的說明),且任意兩個修改操作都是連通的。兩個修改操作 a,ba, b 是連通的,當且僅當存在若干操作 t0,t1,...,tlt_0, t_1, ... , t_l,使得 t0=a,tl=bt_0 = a,t_l = b,且對任意 1il1 \le i \le lti1t_{i−1}tit_i 都是「互相排斥」的修改操作。

一對「互相排斥」的修改操作稱爲互斥對。現在毒瘤想知道,給定值 nnmm 個互斥對,他一共能出出多少道可做的不同的數據結構題。兩個數據結構題是不同的,當且僅當其中某個操作出現在了其中一個題中,但是沒有出現在另一個題中。

題解

爲什麼看到的題解都是虛樹啊?這裏介紹一個 ddp\text{ddp} 做法。

如果 m=n1m=n-1 的話,那就是簡單 dp\text{dp}f[u][0/1]f[u][0/1] 表示 uu 這個點選/不選的方案數,轉移顯然。

那現在如果 nmn \le m 的話,由於 mn+1m-n+1 不大所以我們只要減掉不合法情況即可,容易想到容斥。即枚舉狀態然後把狀態中爲 11 的邊的兩端強制選(即 f[u][0]=0f[u][0]=0 ),然後再做 dp\text{dp} 即可,可是這樣是 O(2mn+1n)O(2^{m-n+1}n) 的過不去。然後我們發現轉移可以寫成矩陣的形式,於是就可以用樹剖+線段樹維護矩陣乘即可,效率 O(2mn+1log2n+n)O(2^{m-n+1}log^2n+n)

代碼

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+15,M=N<<1,P=998244353;
int n,m,hd[N],V[M],nx[M],t=1,top[N],sz[N],dp[N],fa[N];
int son[N],f[N][2],h[N],df[N],id[N],b[15],g[N][2],s;
vector<int>G[N][2];bool vis[N];
struct O{int p[2][2];}W,a[N<<2];
int X(int x){return x>=P?x-P:x;}
int K(int x,int y){
	int z=1;
	for (;y;y>>=1,x=1ll*x*x%P)
		if (y&1) z=1ll*z*x%P;
	return z;
}
O Mul(O A,O B){
	for (int i=0;i<2;i++)
		for (int j=0;j<2;j++){
			W.p[i][j]=0;
			for (int k=0;k<2;k++)
				W.p[i][j]=X(W.p[i][j]+1ll*A.p[k][j]*B.p[i][k]%P);
		}
	return W;
}
void add(int u,int v){
	nx[++t]=hd[u];V[hd[u]=t]=v;
}
void dfs(int u,int fr){
	dp[u]=dp[fa[u]=fr]+1;sz[u]=1;vis[u]=1;
	for (int v,i=hd[u];i;i=nx[i]){
		if (vis[v=V[i]]) continue;
		dfs(v,u);sz[u]+=sz[v];
		if (sz[v]>sz[son[u]]) son[u]=v;
	}
}
void Dfs(int u,int tp){
	top[u]=tp;h[u]=df[id[u]=++t]=u;
	if (son[u]) Dfs(son[u],tp),h[u]=h[son[u]];
	for (int i=hd[u];i;i=nx[i])
		if (V[i]!=son[u] && fa[V[i]]==u)
			Dfs(V[i],V[i]);
}
void Dp(int u){
	f[u][0]=f[u][1]=g[u][0]=g[u][1]=1;
	for (int v,i=hd[u];i;i=nx[i])
		if (fa[v=V[i]]==u) Dp(v),
		f[u][0]=1ll*f[u][0]*(f[v][0]+f[v][1])%P,
		f[u][1]=1ll*f[u][1]*f[v][0]%P;
}
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
void build(int k,int l,int r){
	if (l==r){
		int u=df[l];
		for (int i=hd[u];i;i=nx[i])
			if (V[i]!=son[u] && fa[V[i]]==u)
				g[u][0]=1ll*g[u][0]*(f[V[i]][0]+f[V[i]][1])%P,
				g[u][1]=1ll*g[u][1]*f[V[i]][0]%P;
		a[k]=(O){g[u][0],g[u][0],g[u][1],0};return;
	}
	build(Ls,l,mid);build(Rs,mid+1,r);
	a[k]=Mul(a[Rs],a[Ls]);
}
O qry(int k,int l,int r,int L,int R){
	if (L<=l && r<=R) return a[k];
	if (mid<L) return qry(Rs,mid+1,r,L,R);
	if (mid>=R) return qry(Ls,l,mid,L,R);
	return Mul(qry(Rs,mid+1,r,L,R),qry(Ls,l,mid,L,R));
}
void upd(int k,int l,int r,int x,int v0,int v1){
	if (l==r){a[k]=(O){v0,v0,v1,0};return;}
	if (mid>=x) upd(Ls,l,mid,x,v0,v1);
	else upd(Rs,mid+1,r,x,v0,v1);
	a[k]=Mul(a[Rs],a[Ls]);
	
}
void del(int x,int o){
	O v;int v0,v1,u;
	while(x!=1){
		v=qry(1,1,n,id[x],id[h[x]]);
		v0=v.p[0][0],v1=v.p[1][0],u=fa[x];
		if (!o) G[u][0].push_back(g[u][0]),G[u][1].push_back(g[u][1]);
		g[u][0]=1ll*g[u][0]*K(X(v0+v1),P-2)%P;
		g[u][1]=1ll*g[u][1]*K(v0,P-2)%P;x=top[u];
	}
}
void ins(int x,int o){
	O v;int v0,v1,u;
	while(x!=1){
		v=qry(1,1,n,id[x],id[h[x]]);
		v0=v.p[0][0],v1=v.p[1][0],u=fa[x];
		if (o){
			int z=G[u][0].size();
			g[u][0]=G[u][0][z-1];G[u][0].pop_back();
			g[u][1]=G[u][1][z-1];G[u][1].pop_back();
		}
		else
			g[u][0]=1ll*g[u][0]*(v0+v1)%P,
			g[u][1]=1ll*g[u][1]*v0%P;
		upd(1,1,n,id[u],g[u][0],g[u][1]);
		x=top[u];
	}
}
int main(){
	cin>>n>>m;
	for (int u,v,i=1;i<=m;i++)
		scanf("%d%d",&u,&v),
		add(u,v),add(v,u);
	t=0;dfs(1,0);Dfs(1,1);
	Dp(1);build(1,1,n);t=0;
	for (int u,v,i=1;i<=m;i++){
		u=V[i<<1],v=V[i<<1|1];
		if (dp[u]>dp[v]) swap(u,v);
		if (fa[v]!=u) b[t++]=i;
	}
	
	for (int F,i=(1<<t)-1;~i;i--){
		F=1;
		for (int j=0;j<t;j++)
			if ((i>>j)&1){
				F=P-F;
				for (int u,k=0;k<2;k++){
					u=V[b[j]<<1|k];del(top[u],0);
					G[u][0].push_back(g[u][0]);
					G[u][1].push_back(g[u][1]);
					upd(1,1,n,id[u],g[u][0]=0,g[u][1]);
					ins(top[u],0);
				}
			}
		O v=qry(1,1,n,1,id[h[1]]);
		s=X(1ll*F*(v.p[0][0]+v.p[1][0])%P+s);
		for (int z,j=t-1;~j;j--)
			if ((i>>j)&1){
				F=P-F;
				for (int u,k=0;k<2;k++){
					u=V[b[j]<<1|k];del(top[u],1);
					z=G[u][0].size();
					g[u][0]=G[u][0][z-1];G[u][0].pop_back();
					g[u][1]=G[u][1][z-1];G[u][1].pop_back();
					upd(1,1,n,id[u],g[u][0],g[u][1]);
					ins(top[u],1);
				}
			}
	}
	cout<<s<<endl;return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章