省选联测 31
啊啊啊今天又保龄了。
今天的题确实很适合倒序开题。然后我正序开的导致半个小时没时间写 T3。
T3 的一些实现比较震撼,所以写点题解。(老是过来人问我 T3 是什么东西所以先写了)
shik 吞进去又吐出来。抵德和 ber ♂ 宇纠缠个不清。天空中的素数还没有降下。
你站在椭圆的一 ♂ 焦,伟大尼特向你发问。
苯为
一看这题求图的点染色方案数然后一脸不可做的样子那就套色多项式吧。
色多项式是什么东西?
以下定义抄自 EI 论文。
对于无向图 \(G=(V,E)\),存在多项式 \(P_G(k)\) 使得代入正整数 \(k\) 时 \(P_G(k)\) 的值表示 \(G\) 的 \(k\)-染色方案。
那么答案就是 \(P_G(k)\)。我们有结论:色多项式可以表示为 Tutte 多项式
其中 Tutte 多项式为
其中 \(k(E)\) 表示图 \((V,E)\) 的联通分量数。
那么代个数进去。以下 \(n\) 为原图节点数(即 \(n(A+1)\)),\(c\) 为颜色数(即\(k\)),\(k\) 为环长(即 \((dis(s,t)+1)(A+1)\))。
然后我们发现原图是个基环树。那么可以枚举选了几条边,设选了 \(i\) 条边,分情况讨论:
- 选环:方案数 \(\dbinom{n-k}{i-k}\),连通块数量 \(n-i+1\)。
- 不选环:方案数 \(\dbinom ni-\dbinom{n-k}{i-k}\),连通块数量 \(n-i\)。
那么通通加起来,对于一种环长 \(k\) 的答案就是:
那累计所有环长的贡献就能找到答案。然后关于怎么一遍 dfs 就找到所有贡献的问题,我们分离一下无关变量。首先前边的 \((c-1)^n\) 和 \(k\) 无关,累加 \(n^2\) 次直接记录进答案。然后看后边的其实就是
上边系数和 \(k\) 没关系提出来,那么设 \(pw_k=\dfrac 1{(1-c)^k}\),只要计算 \(\sum pw_k\)。
考虑统计所有路径的贡献。发现我们可以把子树内的路径分为两类:从根节点到子树内部的和跨越根节点的。那么我们统计一下子树答案的前缀和 \(sum\),每次扫到一棵新的子树的时候加上它的答案乘上 \(2\times sum\)(因为来回两条路径),累加前缀和(注意加上根节点的一次)即可。
#include <iostream>
#include <algorithm>
#include <cstdio>
#define int long long
using namespace std;
const int mod=421969921;
int n,A,k,ans,pw[1000010];
struct node{
int v,next;
}edge[2000010];
int t,head[1000010];
void add(int u,int v){
edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
int dfs(int x,int f){
ans=(ans+pw[1])%mod;int sum=pw[1];
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f){
int ret=dfs(edge[i].v,x);
ans=(ans+2ll*sum%mod*ret)%mod;
sum=(sum+1ll*ret*pw[1])%mod;
}
}
return sum;
}
signed main(){
freopen("ber.in","r",stdin);
freopen("ber.out","w",stdout);
scanf("%lld%lld%lld",&n,&A,&k);A++;k%=mod;A%=mod-1;
for(int i=1;i<n;i++){
int u,v;scanf("%lld%lld",&u,&v);
add(u,v);add(v,u);
}
pw[0]=1;pw[1]=qpow(qpow(1-k+mod,mod-2),A);
for(int i=2;i<=n;i++)pw[i]=1ll*pw[i-1]*pw[1]%mod;
dfs(1,0);
ans=1ll*ans*qpow(k-1,(1ll*A*n+1)%(mod-1));
ans=(ans+1ll*qpow(k-1,1ll*A*n%(mod-1))*n%mod*n)%mod;
printf("%lld\n",ans);
return 0;
}