【loj2983】【WC2019】数树
题目
两颗\(n\)个点的树T1和T2,有\(y\)种颜色;
现在给每个点染色,要求公共边端点的颜色相同,求:
1.op=0 , T1和T2都确定,求合法染色方案数;
2.op=1 , T1确定,求所有T2的合法染色方案数的和 ;
3.op=2 , 求所有(T1,T2)合法染色方案数的和;
\(mod \ 998244353\)的值;
$1 \le n \le 10^5 \ , \ 1 \le y \lt 998244353 \ , \ op = { 0,1,2 } $ ;
题解
op=0
map即可
op=1
Part 1
子集反演:
\[g(S) = \sum_{S \subset T} f(T) \Leftrightarrow
f(S) = \sum_{S \subset T} (-1)^{|T|-|S|}f(T) \\
\]设\(T_1 \cap T_2 = S\)的方案数为\(f(S)\),\(T_1 \cap T_2 \supset S\)的方案数为\(g(S)\);
套用得:
\[\begin{align}
ans &= \sum_{S} f(S) y^{n-|S|}\\
&= y^n \sum_{S} y^{-|S|} \sum_{S \subset T}(-1)^{|T|-|S|} g(T) \\
&= y^n \sum_{T} g(T) \sum_{S\subset T}y^{-|S|}(-1)^{|T|-|S|} \\
&= y^n \sum_{T} g(T) \sum_{i=0}^{|T|} (^{|T|}_{\ i})(\frac{1}{y})^{i}(-1)^{|T|-i}\\
&= y^n \sum_{T} g(T) (\frac{1}{y}-1)^{|T|} \\
令z&= \frac{1}{y}-1\\
ans &= y^n \sum_{T} z^{|T|}g(T) \\
\end{align}
\]
Part 2
prufer序列求\(g(T)\):
\(T\)会形成\(m = n-|T|\)个连通块,设大小为\(a_i\),\(g(T)\)相当于将其重新连成树的方案数:
\[g(T) = \sum_{\sum d_i = m-2} (m-2)! \Pi_{i=1}^{m}\frac{a_i^{d_i+1}}{d_i!}
\]考虑先构造一个prufer序列,再在最后依次加入\(1-m\)得到一个长度为\(2m-2\)的度数序列;
\(g(T)\)的组合意义就是对这些点染色,\(i\)号连通块有\(a_i\)种颜色可以染;
对前m-2个位置有n中不同的颜色,后m个位置每个有\(a_i\)种颜色,即:
\[\begin{align}
g(T) &= n^{m-2}\Pi_{i=1}^{m} a_i \\
\Rightarrow \ ans &= y^n \sum_{T}z^{n-m} \times n^{m-2}\Pi_{i=1}^{m} a_i\\
\ ans &= \frac{y^n z^n}{n^2} \sum_{T}z^{-m}n^{m}\Pi_{i=1}^{m} a_i
\end{align}
\]把\(\Pi_{i=1}^{m} a_i\)看成每个连通块选一个点,\(f_{ \{ u,0/1 \} }\)表示是否选了的贡献即可\(dp\);
op=2
Part 1
- 同理:
\[\begin{align}
ans &= y^n\sum_{T} z^{|T|} g^2(T) \\
&= \frac{y^n z^n}{n^4} \sum_{T}z^{-m}n^{2m}\Pi_{i=1}^{m} a_i^2 \\
\end{align}
\]\(\sum_{T}\)后面那一坨可以看成是\(m\)个联通块组成的图;
连通块的EGF为\(F(x) \ = \ \frac{n^2}{z} i^2 \times i^{i-2} \times \frac{x^i}{i!} \Rightarrow \frac{n^2i^i}{zi!}x^i\)
所以图的EGF为$G(x) = e^{F(x)} \Rightarrow ans = \frac{y^n z^n n!}{n^4} [x^n]G(x) $
(这个策爷的论文里有讲)
#include<bits/stdc++.h>
#define ll long long
#define mk make_pair
#define mod 998244353
using namespace std;
const int N=100010;
int n,Y,Z,op;
char gc(){
static char*p1,*p2,s[1000000];
if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
return(p1==p2)?EOF:*p1++;
}
int rd(){
int x=0;char c=gc();
while(c<'0'||c>'9')c=gc();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
return x;
}
int pw(int x,int y){
int re=1;
if(y<0)y+=mod-1;
while(y){
if(y&1)re=(ll)re*x%mod;
y>>=1;x=(ll)x*x%mod;
}
return re;
}
namespace subtask0{
typedef pair<int,int>pii;
map<pii,bool>mp;
void solve(){
for(int i=1;i<n;++i){
int u=rd(),v=rd();
if(u>v)swap(u,v);
mp[mk(u,v)]=1;
}
int cnt=0;
for(int i=1;i<n;++i){
int u=rd(),v=rd();
if(u>v)swap(u,v);
if(mp[mk(u,v)])cnt++;
}
printf("%d\n",pw(Y,n-cnt));
}
}
namespace subtask1{
int f[N][2],o=1,hd[N],pre,ipre;
struct Edge{int v,nt;}E[N<<1];
void adde(int u,int v){
E[o]=(Edge){v,hd[u]};hd[u]=o++;
E[o]=(Edge){u,hd[v]};hd[v]=o++;
}
void dfs(int u,int fa){
f[u][0]=f[u][1]=pre;
for(int i=hd[u];i;i=E[i].nt){
int v=E[i].v;
if(v==fa)continue;
dfs(v,u);
int t0 = f[u][0] , t1 = f[u][1];
f[u][0] = (1ll * t0 * f[v][1] %mod + 1ll * t0 * f[v][0] %mod * ipre %mod) %mod;
f[u][1] = (1ll * t1 * f[v][1] %mod + 1ll * (1ll*t0*f[v][1]+1ll*t1*f[v][0]) %mod * ipre %mod) %mod;
}
}
void solve(){
if(!Z){cout<<pw(n,n-2)<<endl;return;}
for(int i=1;i<n;++i)adde(rd(),rd());
pre = 1ll * n * pw(Z , mod-2) %mod;
//pre = 1;
ipre = pw(pre , mod-2);
dfs(1,0);
int ans = 1ll * f[1][1] * pw(1ll*Y*Z%mod , n) %mod * pw(1ll*n*n%mod , mod-2) %mod;
cout << ans << endl;
}
}
namespace subtask2{
const int N2=N<<2;
int fac[N2],ifac[N2],iv[N2],a[N2],b[N2],L,G=3,rev[N2];
void ntt(int*A,int len,int f){
for(L=0;(1<<L)<len;++L);
for(int i=0;i<len;++i){
rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
if(i<rev[i])swap(A[i],A[rev[i]]);
}
for(int i=1;i<len;i<<=1){
int wn=pw(G , f * (mod-1)/i/2);
for(int j=0;j<len;j+=i<<1){
int w=1;
for(int k=0;k<i;++k,w=1ll*w*wn%mod){
int x=A[j+k],y=1ll*w*A[j+k+i]%mod;
A[j+k]=(x+y)%mod,A[j+k+i]=(x-y+mod)%mod;
}
}
}
if(!~f)for(int i=0;i<len;++i)A[i]=(ll)A[i]*iv[len]%mod;
}
void cls(int*A,int l,int r){for(int i=l;i<r;++i)A[i]=0;}
void cpy(int*A,int*B,int l){for(int i=0;i<l;++i)A[i]=B[i];}
void der(int*A,int l){for(int i=0;i<l-1;++i)A[i]=1ll*A[i+1]*(i+1)%mod;A[l-1]=0;}
void dif(int*A,int l){for(int i=l-1;i;--i)A[i]=1ll*A[i-1]*iv[i]%mod;A[0]=0;}
void inv(int*A,int*B,int l){
if(l==1){B[0]=1;return;}
static int t[N2];
int len=l<<1;
inv(A,B,l>>1);cls(B,l,len);
cpy(t,A,l);cls(t,l,len);
ntt(B,len,1);ntt(t,len,1);
for(int i=0;i<len;++i)B[i] = B[i] * (2 - 1ll * t[i] * B[i] %mod +mod) %mod;
ntt(B,len,-1);cls(B,l,len);
}
void ln(int*A,int*B,int l){
static int t[N2];
int len=l<<1;
inv(A,B,l);
cpy(t,A,l);cls(t,l,len);der(t,l);
ntt(B,len,1);ntt(t,len,1);
for(int i=0;i<len;++i)B[i] = 1ll * B[i] * t[i] %mod;
ntt(B,len,-1);cls(B,l,len);
dif(B,l);
}
void exp(int*A,int*B,int l){
if(l==1){B[0]=1;return;}
static int t[N2];
int len=l<<1;
exp(A,B,l>>1);cls(B,l,len);
ln(B,t,l);
for(int i=0;i<l;++i)t[i] = ( -t[i] + A[i] + mod)%mod;
t[0]++;//!!!
ntt(B,len,1);ntt(t,len,1);
for(int i=0;i<len;++i)B[i] = 1ll * B[i] * t[i] %mod;
ntt(B,len,-1);cls(B,l,len);
}
void solve(){
if(!Z){cout<<pw(n,2*n-4)<<endl;return ;}
int len=1;for(;len<=2*n;len<<=1);
iv[1]=1;for(int i=2;i<=len;++i)iv[i]=1ll*(mod-mod/i)*iv[mod%i]%mod;
for(int i=fac[0]=ifac[0]=1;i<=len;++i){
ifac[i]=(ll)ifac[i-1]*iv[i]%mod;
fac[i]=(ll)fac[i-1]*i%mod;
}
int tmp = 1ll * n * n %mod * pw(Z,mod-2) %mod;
for(int i=1;i<=n;++i)a[i] = 1ll * tmp * pw(i,i) %mod * ifac[i] %mod ;
exp(a, b, len>>1 );
int ans = 1ll * pw(1ll*Y*Z%mod , n) * pw((ll)n*n%mod*n%mod*n%mod , mod-2) %mod * fac[n] %mod * b[n] %mod;
cout << ans << endl;
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=rd();Y=rd();Z=pw(Y,mod-2)-1;op=rd();
if(op==0)subtask0::solve();
else if(op==1)subtask1::solve();
else subtask2::solve();
return 0;
}
【loj2983】【WC2019】数树的更多相关文章
- [LOJ2983] [WC2019] 数树
题目链接 LOJ:https://loj.ac/problem/2983 BZOJ:https://lydsy.com/JudgeOnline/problem.php?id=5475 洛谷:https ...
- [WC2019] 数树
[WC2019] 数树 Zhang_RQ题解(本篇仅概述) 前言 有进步,只做了半天.... 一道具有极强综合性的数数好题! 强大的多合一题目 精确地数学推导和耐心. 有套路又不失心意. 融合了: 算 ...
- 洛谷 P5206: bzoj 5475: LOJ 2983: [WC2019] 数树
一道技巧性非常强的计数题,历年WC出得最好(同时可能是比较简单)的题目之一. 题目传送门:洛谷P5206. 题意简述: 给定 \(n, y\). 一张图有 \(|V| = n\) 个点.对于两棵树 \ ...
- 并不对劲的bzoj5475:loj2983:p5206:[wc2019]数树
题目大意 task0:有两棵\(n\)(n\leq10^5)个点的树\(T1,T2\),每个点的点权可以是一个在\([1,y]\)里的数,如果两个点既在\(T1\)中有直接连边,又在\(T2\)中有直 ...
- 洛谷P5206 [WC2019]数树 [容斥,DP,生成函数,NTT]
传送门 Orz神仙题,让我长了许多见识. 长式子警告 思路 y=1 由于y=1时会导致后面一些式子未定义,先抓出来. printf("%lld",opt==0?1:(opt==1? ...
- 洛谷P5206 [WC2019] 数树(生成函数+容斥+矩阵树)
题面 传送门 前置芝士 矩阵树,基本容斥原理,生成函数,多项式\(\exp\) 题解 我也想哭了--orz rqy,orz shadowice 我们设\(T1,T2\)为两棵树,并定义一个权值函数\( ...
- 【LuoguP5206】[WC2019] 数树
题目链接 题意 定义 \(F(T_1,T_2)=y^{n-common}\) 其中 \(common\) 为两棵树 \(T_1,T_2\) 的公共边条数. 三种问题 1.给定 \(T_1,T_2\) ...
- 洛谷 P5206 - [WC2019]数树(集合反演+NTT)
洛谷题面传送门 神仙多项式+组合数学题,不过还是被我自己想出来了( 首先对于两棵树 \(E_1,E_2\) 而言,为它们填上 \(1\sim y\) 使其合法的方案数显然是 \(y\) 的 \(E_1 ...
- BZOJ5475 WC2019数树(prufer+容斥原理+树形dp+多项式exp)
因为一大堆式子实在懒得写题解了.首先用prufer推出CF917D用到的结论,然后具体见前言不搭后语的注释. #include<iostream> #include<cstdio&g ...
- [题解][P5206][WC2019] 数树 (op = 1)
简要题意 给定 \(n, y\). 一张图有 \(|V| = n\) 个点,现在给出两棵树 \(T_1=G(V, E_1)\) 和 \(T_2=G(V, E_2)\). 定义这两棵树的权值 \(F(E ...
随机推荐
- HTML5+规范:nativeUI(管理系统原生界面)
nativeUI管理系统原生界面,可用于弹出系统原生提示对话框窗口.时间日期选择对话框.等待对话框等. 1.方法 1.1.actionSheet: 弹出系统选择按钮框 void plus.native ...
- 用C#做一个 拉流播放器
做拉流播放器第一个想到就是,.,..FFmpeg没错 我也是用强大的他它来做的.但是我用的不是 cmd 调用 而是用的强大的FFmpeg.AutoGen FFmpeg.AutoGen 这个是C# 一 ...
- spring Boot 学习(三、Spring Boot与检索)
一.检索我们的应用经常需要添加检索功能,开源的 ElasticSearch 是目前全文搜索引擎的 首选.他可以快速的存储.搜索和分析海量数据.Spring Boot通过整合Spring Data El ...
- 把项目通过maven生产源码包和文档包并发布到自己的私服上
<!-- 把项目通过maven生产源码包和文档包并发布到自己的私服上 执行maven命令,mvn clean package,执行完成后 命令:mvn deploy 就可以发布到你自己的私服上了 ...
- centos7安装face_recognition踩各种坑
要在阿里云服务器上部署face_recognition.用的是centos7. 千辛万苦啊.感谢网上的各种解答.回报社会,我也把各种坑写下了.整理的有点乱.不过仔细看,有干货的. 感谢这个博主Fat ...
- 关于AWK的10个经典案例
awk是Linux系统下一个处理文本的编程语言工具,能用简短的程序处理标准输入或文件.数据排序.计算以及生成报表等等,应用非常广泛.基本的命令语法:awk option 'pattern {actio ...
- poi读取excel的列和删除列
(各自根据具体的poi版本进行相应的替换即可) package com.br.loan.strategy.common.utils; import lombok.extern.slf4j.Slf4j; ...
- Windows VNC远程连接用法
VNC (Virtual Network Console)是虚拟网络控制台 被控端 被控端需要打开服务,等待主控端连接 服务端已经启动成功,右下角有小图标 主控端 打开主控端,连接被控端 输入被控端i ...
- Python学习日记(二十六) 封装和几个装饰器函数
封装 广义上的封装,它其实是一种面向对象的思想,它能够保护代码;狭义上的封装是面向对象三大特性之一,能把属性和方法都藏起来不让人看见 私有属性 私有属性表示方式即在一个属性名前加上两个双下划线 cla ...
- Android笔记(七十五) Android中的图片压缩
这几天在做图记的时候遇第一次遇到了OOM,好激动~~ 追究原因,是因为在ListView中加载的图片太大造成的,因为我使用的都是手机相机直接拍摄的照片,图片都比较大,所以在加载的时候会出现内存溢出,那 ...