P8315 [COCI2021-2022#4] Šarenlist 题解
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;
}