zoukankan      html  css  js  c++  java
  • jzoj5988. 【WC2019模拟2019.1.4】珂学计树题 (burnside引理)

    传送门

    题面

    liu_runda曾经是个喜欢切数数题的OIer,往往看到数数题他就开始刚数数题.于是liu_runda出了一个数树题.听说OI圈子珂学盛行,他就在题目名字里加了珂学二字.一开始liu_runda想让选手数n个节点的不同构的二叉树的数目.
    但是liu_runda虽然退役已久,也知道答案就是Catalan(n),这太裸了,出出来一定会被挂起来裱.因此他把题目加强.我们从二叉树的根节点出发一直向右儿子走到不能再走为止,可以找到最右下方的节点v,这个节点是没有右儿子的.
    如果根节点和v不相同,我们就把根节点和根节点的右儿子断开,让根节点的右儿子成为新的根节点,同时把根节点接在v的右儿子位置.根节点的左儿子此时仍然挂在根节点上.
    这样的操作可以进行多次.如果两棵二叉树能通过若干次这样的操作变得同构,我们也认为它们是同构的.
    问在这种新的定义下有多少n个节点的本质不同的二叉树.答案可能很大,所以只需要输出对998244353取模后的结果.

    题解

    全场切就我一个不会的……话说我对(burnside)理解还是太浅啊……

    我们对于一棵二叉树定义一个括号表示法,其中第一个左括号和第一个右括号之间的是根节点的左子树,后面的全都是根节点的右子树。如果某一对匹配的括号满足不存在其他匹配的括号包含它们,我们就称其为“顶级括号”。

    从二叉树的根节点出发向右儿子方向一直走到没有右儿子时,经过的一条链我们不妨称作“右侧链”。根据刚刚的定义,每个顶级括号都对应右侧链上的一个节点。而每个顶级括号内部的串对应这个节点的左子树。

    这样的话(n)个节点的二叉树就可以用(n)个左括号和(n)个右括号组成的合法的括号序列来表示了,可以转化为数“循环移位后相同则本质相同”的括号序列个数

    这里有个小问题,比如((()))移位后变成()(()),后者不合法,说明这个交换群不满足封闭性

    于是这里有个结论:(n)(0)(n)(1)(01)序列“循环移位后相同则本质相同”的等价类个数和(n)对合法括号序列的等价类一一对应

    显然每个合法的括号序列都可以转化为(01)序列。而对于每个(01)序列,都可以经过若干次循环移位后得到一个“任意前缀中(0)的个数不少于(1)”的序列,就可以把它转化为一个合法的括号序列

    所以现在只要数(01)序列的不同等价类个数就好了。根据(burnside)引理,等价类个数为不动点总数的平均值。

    往右旋转(0)次不变,每个点都不变,不动点个数为({2nchoose n})

    往右旋转(1)次不变,需要任意两个相邻位置颜色相等,不存在

    往右旋转(2)次不变,那么奇数位置都相同,偶数位置都相同,有2种

    往右旋转(m)次不变,那么记(G=gcd(2n,m),G,2G,3G…)这些位置都相同,(1,G+1,2G+1…)这些位置都相同,因此我们只需要考虑前(G)个位置如何排列。只有当(G)为偶数的时候有({Gchoose G/2})个不动点

    于是就可以做到(O(nlog n))计算了,其中(log)是计算(gcd)的复杂度(也可以直接(O(n))预处理欧拉函数然后搞,不过懒了2333)

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
    #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    using namespace std;
    const int N=2e6+5,P=998244353;
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
    	return res;
    }
    int fac[N],inv[N],n,res,lim,g;
    inline int C(R int n,R int m){if(m>n)return 0;return 1ll*fac[n]*inv[m]%P*inv[n-m]%P;}
    inline int gcd(R int x,R int y){if(!y)return x;while(y^=x^=y^=x%=y);return x;}
    int main(){
    	freopen("tree.in","r",stdin);
    	freopen("tree.out","w",stdout);
    	scanf("%d",&n),lim=n<<1;
    	fac[0]=inv[0]=1;fp(i,1,lim)fac[i]=mul(fac[i-1],i);
    	inv[lim]=ksm(fac[lim],P-2);fd(i,lim-1,1)inv[i]=mul(inv[i+1],i+1);
    	for(R int i=0;i<=lim-1;i+=2)g=gcd(lim,i),res=add(res,C(g,g/2));
    	printf("%d
    ",mul(res,ksm(lim,P-2)));return 0;
    }
    
  • 相关阅读:
    Swift
    ios高质量博客
    Swift
    UML建模
    Swift
    Swift
    IIS建立.net framework4 应用程序池HTTP 错误 500.21
    zz entity framework vs linq to sql
    zz部署wcf iis
    zzIIS站点中部署WCF项目
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10223284.html
Copyright © 2011-2022 走看看