QOJ856 Cactus 广义串并联图
题意
给定一棵仙人掌, 你需要用 \(k\) 种颜色给每个结点染色, 且保证有边相连的结点的颜色不相同, 求染色的方案数对 \(10^9+7\) 取模的结果。仙人掌定义为一张特殊的无向图, 其中每条边至多在一个简单环上。
题解
因为是仙人掌,考虑广义串并联图方法。
对每条广义串并联图上的边,记 \(f,g\) 分别表示已确定两端点颜色,两端点颜色相同与两端点颜色不同的方案数。考虑如何压缩:
- 叠合重边:\(f,g\) 分别相乘即可。
- 删 \(1\) 度点:将答案乘上 \(f+(k-1)g\) 即可。
- 删 \(2\) 度点:记两条边分别为 \(u,v\),则合并以后边的 \(f=f_uf_v+g_ug_v(k-1),\ g=f_ug_v+g_uf_v+g_ug_v(k-2)\)。
最后再将答案乘上 \(k\) 就做完了。
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define mxn 300003
#define md 1000000007
#define pb push_back
#define mkp make_pair
#define ld long double
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int T,n,m,k;
map<int,pair<ll,ll>>mp[mxn];
queue<int>q;
ll ans;
inline void add(int x,int y,ll f,ll g){
if(mp[x].find(y)!=mp[x].end()){
auto d=mp[x][y];
mp[x][y]=mp[y][x]={f*d.first%md,g*d.second%md};
}else mp[x][y]=mp[y][x]={f,g};
}
void solve(){
scanf("%d%d%d",&n,&m,&k);
rep(i,1,n)mp[i].clear();
ans=1;
for(int i=0,x,y;i<m;++i){
scanf("%d%d",&x,&y);
add(x,y,0,1);
}
rep(i,1,n)if(mp[i].size()<3)q.push(i);
while(q.size()){
int x=q.front();q.pop();
if(mp[x].size()==1){
ans=(mp[x].begin()->second.first+mp[x].begin()->second.second*(k-1))%md*ans%md;
}else if(mp[x].size()==2){
auto it=mp[x].begin();
int y1=it->first;auto z1=it->second;it++;
int y2=it->first;auto z2=it->second;
add(y1,y2,(z1.first*z2.first+z1.second*z2.second%md*(k-1))%md,(z1.first*z2.second+z1.second*z2.first+z1.second*z2.second%md*(k-2))%md);
}
for(auto i:mp[x]){
mp[i.first].erase(x);
if(mp[i.first].size()<3)q.push(i.first);
}
mp[x].clear();
}
cout<<ans*k%md<<'\n';
}
signed main(){
scanf("%d",&T);
while(T--)solve();
return 0;
}

浙公网安备 33010602011771号