「PKUWC2018」随机游走

题面在这里!

    显然你如果直接带一个子集到树上dp的话复杂度是会炸上天的23333.

    考虑期望也是可以进行min_max容斥的,也就是: max{S} = ∑ min{T} * (-1) ^( |T|+1 ) ,其中T是S的一个非空子集,max{S}和min{S}分别代表集合中所有条件都被满足的期望时间 和 集合中至少有一个条件被满足的期望时间, 当然对本题来说就是 所有钦定的点都被到过一次的期望时间 和 第一次到某个钦定的点的期望时间。。。。

    发现min非常的好算,对于每个集合直接一次树上dp O(n * log 998244351) 就可以算出来了(千万不要用高斯消元。。。这种树上路径期望问题可以把每个节点的dp值表示 a * f(fa) + b的形式,dfs的时候直接算就行了)

    然后把每个集合的min{}算出来之后就可以用类FMT的逆推方法推出每个集合的答案。

    不过两者之间不同的是,FMT的式子是: f '(S') = ∑ f(S) * (-1) ^ ( |S'| - |S|) ,而这个的是 f '(S') = ∑ f(S) * (-1) ^ ( |S|+1 )。

    但其实并没有什么难度,因为我们把 S' 中1的个数按奇偶性讨论一下就可以直接在FMT之后的数组上修改成我们要的样子了。。。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define pb push_back
const int ha=998244353,N=300005;

inline int read(){
	int x=0; char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
	return x-1;
}

inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}

int ksm(int x,int y){
	int an=1;
	for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
	return an;
}

vector<int> g[23];
int n,Q,root,ans[N],a[23],b[23],S,d[23];
bool bt[N];

void dfs(int x,int fa){
	a[x]=d[x],b[x]=1; int tmp=1;
	
	for(int i:g[x]) if(i!=fa){
		dfs(i,x),ADD(tmp,ha-a[i]*(ll)d[x]%ha);
		ADD(b[x],b[i]*(ll)d[x]%ha);
	}
	
	tmp=ksm(tmp,ha-2);
	a[x]=a[x]*(ll)tmp%ha;
	b[x]=b[x]*(ll)tmp%ha;
	
	if((1<<x)&S) a[x]=b[x]=0;
}

inline void solve(){
	bt[0]=1; for(int i=1;i<(1<<n);i++) bt[i]=!bt[i^(i&-i)];
	
	for(int i=0;i<n;i++)
	    for(int j=(1<<n)-1;j>=0;j--) if(!((1<<i)&j)) ADD(ans[j|(1<<i)],ha-ans[j]);
	
	for(int i=(1<<n)-1;i>=0;i--) if(bt[i]) ans[i]=ha-ans[i];

    for(int k,s;Q;Q--){
    	scanf("%d",&k),s=0;
    	while(k--) s|=1<<read();
    	printf("%d
",ans[s]);
	}
}

int main(){
	scanf("%d%d",&n,&Q),root=read();
	int uu,vv;
	for(int i=1;i<n;i++){
		uu=read(),vv=read();
		g[uu].pb(vv),g[vv].pb(uu);
		d[uu]++,d[vv]++;
	}
	
	for(int i=0;i<n;i++) d[i]=ksm(d[i],ha-2);
	
	for(S=1;S<(1<<n);S++){
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		
		dfs(root,-1),ans[S]=b[root];
	}
	
	solve();
	
	return 0;
} 

  

原文地址:https://www.cnblogs.com/JYYHH/p/9194873.html