zoukankan      html  css  js  c++  java
  • Luogu P6277 [USACO20OPEN]Circus P

    Link
    特殊的,(k=n)时答案为(n!)
    如果忽略标号的话,那么任意两个状态都是可以相互到达的。
    因此我们考虑固定(k)个位置,计算有多少种标号排列的等价类。
    由群论的基本知识可以发现,所有等价类的大小都是相同的,因此答案为(frac{k!}{size})
    考虑如何计算等价类大小,一个方法是判断能否交换两个点并使得剩下的不变。
    我们把所有没有分岔,两端都挂着一个连通块的链(包括端点)单独拿出来。
    同一条链上的点是无法交换的,而两端挂的连通块中的点可以交换的充要条件是链长小于(n-k)
    也就是说两个点可以交换的充要条件就是这两点间的路径上不存在长度不小于(n-k)的链。
    如果我们把所有可以相互交换的点对连边,设连完边之后每个连通块的大小为(s_i),那么(size=prod s_i),注意这个连通块大小是要去掉链上的点的。
    那么我们从大到小枚举(k),用并查集维护可以相互到达的连通块,每次把长度小于(n-k)的链的两端的连通块接起来即可。
    因为连通块数和链数同阶,而一条链会被统计链长次,所有连通块的枚举次数之和为(O(n))

    #include<set>
    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    const int N=100007,P=1000000007;
    int n,fa[N],size[N],num[N],ifac[N],ans[N];std::vector<int>e[N];std::set<int>root;
    struct edge{int u,v,w;};std::vector<edge>vec;
    int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
    void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
    int mul(int a,int b){return 1ll*a*b%P;}
    int pow(int a,int b){int r=1;for(;b;b>>=1,a=mul(a,a))if(b&1)r=mul(a,r);return r;}
    int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
    void dfs(int u,int fa,int dep,int root){for(int v:e[u])if(v^fa){if(e[v].size()==2)dfs(v,u,dep+1,root);else if(root<v)vec.push_back({root,v,dep+1});}}
    int main()
    {
        int n=read();
        for(int i=ans[0]=ifac[0]=1;i<=n;++i) ifac[i]=pow(ans[i]=mul(ans[i-1],i),P-2);
        for(int i=1,u,v;i<n;++i) u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
        for(int u=1;u<=n;++u) if(e[u].size()^2) num[fa[u]=u]=e[u].size(),dfs(u,0,1,u),size[u]=1,root.insert(u);
        std::sort(vec.begin(),vec.end(),[](edge a,edge b){return a.w>b.w;});
        for(int i=n-1;i;--i)
        {
    	for(int u,v;vec.size()&&vec.back().w+i<n;vec.pop_back())
    	    u=find(vec.back().u),v=find(vec.back().v),size[u]+=size[v]+vec.back().w-2,num[u]+=num[v]-2,root.erase(v),fa[v]=u;
    	for(int x:root) ans[i]=mul(ans[i],ifac[i-n+size[x]+(n-i-1)*num[x]]);
        }
        for(int i=1;i<=n;++i) printf("%d
    ",ans[i]);
    }
    
  • 相关阅读:
    JS正则与PHP正则
    关于微信扫码支付的流程
    Jquery快速入门
    phpstorm快捷键大全
    CentOS 7.3 下部署基于 Node.js的微信小程序商城
    一个故事告诉你比特币的原理及运作机制 (转 2013)
    mysql The used table type doesn’t support FULLTEXT indexes 解决方案 (phpstudy 会出现),coten不会
    linux下使用 du查看某个文件或目录占用磁盘空间的大小
    ◆织梦内容管理系统模板标签代码参考
    Linux 下挂载新硬盘方法(转)
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12730735.html
Copyright © 2011-2022 走看看