P8315 [COCI2021-2022#4] Šarenlist 题解

发布时间 2024-01-06 08:12:58作者: Pengzt

P8315

T3 写太慢了,就没看这道/gg。错过简单题+1。

不好直接对边或路径进行考虑,但是发现 \(m\) 非常小,考虑容斥。

即每次钦定集合 \(S\),强制包含在 \(S\) 内的路径不合法,其它的都可以,容斥系数就是 \(-1^{|S|}\)。每次可以暴力覆盖染色,然后用一个并查集进行维护即可,使用按秩合并和路径压缩两种优化即可做到 \(\mathcal{O}(nm2^m\alpha(m))\)

具体细节见代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
const int mod=1e9+7;
int n,m,k;ll ans;
int fa[100],dep[100],a[100],b[100];
vector<int>e[100];
ll power(ll a,ll b){
	ll res=1;
	for(;b;b>>=1,a=a*a%mod)if(b&1)res=res*a%mod;
	return res;
}
void dfs(int u,int ff){
	dep[u]=dep[ff]+1,fa[u]=ff;
	for(int v:e[u])if(v!=ff)dfs(v,u);
}
int anc[100],vis[100],tim[100];
int find(int x){return anc[x]==x?x:anc[x]=find(anc[x]);}
ll work(int x){
	for(int i=1;i<=max(n,m);i++)anc[i]=tim[i]=vis[i]=0;
	for(int i=0;i<m;i++)if(x>>i&1){
		int u=a[i+1],v=b[i+1];anc[i+1]=i+1;
		while(u!=v){
			if(dep[u]<dep[v])swap(u,v);
			if(!tim[u])tim[u]=i+1;else anc[find(tim[u])]=i+1;
			vis[u]=1;
			u=fa[u];
		}
	}
	int c=0,r=n-1;
	for(int i=1;i<=n;i++)if(fa[i])r-=vis[i];
	for(int i=1;i<=m;i++)if(anc[i])c+=(anc[i]==i);
	return power(k,r)*power(k,c)%mod;
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),e[u].pb(v),e[v].pb(u);
	for(int i=1;i<=m;i++)scanf("%d%d",&a[i],&b[i]);
	dfs(1,0);
	for(int i=0;i<(1<<m);i++)ans=(ans+(__builtin_popcount(i)&1?-1:1)*work(i)+mod)%mod;
	printf("%lld\n",ans);
	return 0;
}