【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;
}

原文地址:https://www.cnblogs.com/Paul-Guderian/p/10801771.html

时间: 2024-08-01 17:10:07

【loj2983】【WC2019】数树的相关文章

[WC2019] 数树

Statement 有\(n\)个节点, 分别用红线,蓝线连成两棵树. 用\(y\)种颜色给节点染色, 规定如果一条边在两棵树中同时出现, 那么边两端的点的颜色必须相同. Task #1: 给定两棵树, 求染色方案. Task #2: 给定其中一棵树, 求对于另一棵树的每一种形态的染色方案数之和. Task #3: 两棵树的形态都没有确定, 求对于所有情况的染色方案数之和. (\(n\le 10^5\)) Solution Task #1 计算有多少条公共边即可. Task #2 如果有\(i\

WC2019 T1 数树

WC2019 T1 数树 传送门(https://loj.ac/problem/2983) Question 0 对于给定的两棵树,设记两颗树 \(A,B\) 的重边为 \(R(A,B)\),那么 \[ Ans=y^{n-R(A,B)} \] Question 1 给定其中一棵树,求第二棵树的所有情况下答案的总和 不妨令 \(y=y^{-1}\) ,最终答案就是 \(y^{-n}y^{R(A,B)}\). 在给定 \(A\) 的情况下,只需要统计 \(\sum\limits_B y^{R(A,B

Thair数 树状数组

题目: Erwin最近对一种叫"thair"的东西巨感兴趣... 在含有n个整数的序列a1,a2......an中, 三个数被称作"thair"当且仅当i<j<k且ai<aj<ak 求一个序列中"thair"的个数. Input (thair.in) 开始一个正整数n, 以后n个数a1~an. Output (thair.out) "thair"的个数 Sample Input Output 4 2 1

1006数树11

小黄山上死掉的树木,上次已经被全部更换上了新的树苗,经过半年左右已经开始茁壮成长.2015级的小伙伴想对小黄山上的数目进行清点,在一个矩形区域内进行数树.横着数有m棵,竖着数有n棵,问总共有多少棵? 输入有多组测试用例.对于每组测试用例,包含两个正整数,分别为m和n(0 < m, n <= 1000). 对于每组测试用例输出一个正整数,矩形区域内总共有多少棵树. 2 6 3 5 12 15 #include"stdio.h"#include"math.h"

【WC2019】数树 树形DP 多项式exp

题目大意 有两棵 \(n\) 个点的树 \(T_1\) 和 \(T_2\). 你要给每个点一个权值吗,要求每个点的权值为 \([1,y]\) 内的整数. 对于一条同时出现在两棵树上的边,这条边的两个端点的值相同. 若 \(op=0\),则给你两棵树 \(T_1,T_2\),求方案数. 若 \(op=1\),则给你一棵树 \(T_1\),求对于所有 \(n^{n-2}\) 种 \(T_2\),方案数之和. 若 \(op=2\),则求对于所有的 \(T_1,T_2\),求方案数之和. \(n\leq

hdu 4911 求逆序对数+树状数组

http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能减少一个逆序对,所以问题转换成如何求逆序对数. 归并排序或者树状数组都可搞 树状数组: 先按大小排序后分别标号,然后就变成了求1~n的序列的逆序数,每个分别查询出比他小的用i减,在把他的值插入即可 #include <cstdio> #include <cstdlib> #includ

求序列中满足Ai &lt; Aj &gt; Ak and i &lt; j &lt; k的组数 树状数组 HIT 2275 Number sequence

http://acm.hit.edu.cn/hoj/problem/view?id=2275 Number sequence   Source : SCU Programming Contest 2006 Final   Time limit : 1 sec   Memory limit : 64 M Submitted : 1632, Accepted : 440 Given a number sequence which has N element(s), please calculate

Hihocoder #1014 : Trie树 (字典数树统计前缀的出现次数 *【模板】 基于指针结构体实现 )

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?” 身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字

[jzoj 3175] 数树数 解题报告 (树链剖分)

interlinkage: https://jzoj.net/senior/#main/show/3175 description: 给定一棵N 个节点的树,标号从1~N.每个点有一个权值.要求维护两种操作:1. C i x(0<=x<2^31) 表示将i 点权值变为x2. Q i j x(0<=x<2^31) 表示询问i 到j 的路径上有多少个值为x的节点 solution: 链剖 把颜色离散化,对每种颜色分别搞一颗线段树 直接搞会炸空间,因此要动态开点 感觉树上莫队好像也可以