zoukankan      html  css  js  c++  java
  • 洛谷P4980 【模板】Polya定理

    洛谷P4980

    1、置换

      置换简单来说就是对元素进行重排列,如下图所示。置换是[1,n]到[1,n]的一一映射。

      举个直观的例子,将正方形绕其中心逆时针旋转90度,可以看成是正方形四个顶点的一个置换。关于置换、置换群的具体理论,请参考其他资料,此处有个大致印象就好。下面描述几个结论。 

      (1)置换可以分解成若干循环,方法为:连边1->a1,2->a2,…,i->ai,…,n->an,任取一个元素,顺着有向边走,直到回到出发点,即形成一个环,剩余元素如法炮制。

      (2)如果一个状态经过置换 f 后跟原来相同,即S[1] = S[a1], S[2] = S[a2], …, S[n] = S[an]。则称该状态为 f 的不动点。

      (3)题目中常常出现“本质不同的方案数”,一般是指等价类的数目,题目定义一个等价关系,满足等价关系的元素属于同一等价类。等价关系通常是一个置换集合F,如果一个置换能把其中一个方案映射到另一个方案,则二者是等价的。

    2、burnside引理

      对于一个置换f,若一个染色方案s经过置换后不变,称s为f的不动点。将f的不动点数目记为C(f),则可以证明等价类数目为所有C(f)的平均值。

     

      如上图(图片来自百度百科“burnside引理”)所示,对于四个置换{逆时针旋转0°,逆时针旋转90°,逆时针旋转180°,逆时针旋转270°},其不动点数分别为16, 2, 4, 2。所以等价类数目为(16+2+4+2)/4 = 6。

    3、polya定理

       polay定理实际上是burnside引理的具体化,提供了计算不动点的具体方法。

      假设一个置换有k个循环,易知每个循环对应的所有位置颜色需一致,而任意两个循环之间选什么颜色互不影响。因此,如果有m种可选颜色,则该置换对应的不动点个数为m^k。用其替换burnside引理中的C(f),得到等价类数目为:

      其中|F|表示置换的数目,ki表示第i个置换包含的循环个数。

    对于此题,把染好色的环从某处断开看成一个序列,置换是:循环右移0位,循环右移1位,..,循环右移n-1位。(并不明白为什么要把循环右移0位放进去,先咕咕咕了)这样就使得同一个环染色方法的不同表示方法属于同一个等价类。

    用burnside引理,得到等价类数目是对于各个置换的不动点数目的平均值。

    就是,总的环染色方法数目$=frac{sum_{i=0}^{n-1}循环右移i位后不变的序列染色方法数目}{n}$。(把统计环染色方法数转化为统计序列染色方法数)

    对于此题而言,置换分解成循环的实际操作举例:

    循环右移0位分解成1(->1),2(->2),..,n(->n)这n个

    循环右移1位分解成1->2->3->..->n(->1)这1个

    循环右移2位,当n是奇数时分解成1->3->5->..->n->2->4->..->n-1(->1)这1个;当n是偶数时分解成1->3->5->..->n-1(->1),2->4->..->n(->2)这2个

    ......

    可以发现,对于此题,循环右移i位的置换,可以分解成gcd(i,n)个循环

    polya:一种序列染色方案a是置换A的不动点,那么A的同一个循环中的所有位置显然都需要有相同的颜色,A的不同循环中的位置的颜色互不影响,那么置换A的不动点个数就是颜色种类数的循环个数次方

    因此此题最终答案是$frac{sum_{i=0}^{n-1}n^{gcd(i,n)}}{n}$

    好像不能直接算,可以化简成$frac{sum_{j|n}n^jvarphi(frac{n}{j})}{n}$,就可以算了

    这个$varphi$的话,可以发现对于每个n,会涉及的所有$varphi(i)$都满足i是n的因子,而且根据线性筛的递推方法分解质因数后递归(记忆化)一下算仍然只会涉及到n的因子;也可以先线性筛一小部分,总之怎么搞都行

    错误记录:calc_phi错了

    卡常记录:

    (不开优化时)将直接计算上限sqrt(x)换成每次判断i*i<=x会更快;

    尽管是64位机子,把某一些特定的longlong换成int能快2/3;

    似乎递归+记忆化算phi没有直接暴力算快

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 #include<cmath>
     6 using namespace std;
     7 #define fi first
     8 #define se second
     9 #define mp make_pair
    10 #define pb push_back
    11 typedef long long ll;
    12 typedef unsigned long long ull;
    13 int T,n,ans;
    14 int prime[20011],len,phi[100011];
    15 char nprime[100011];
    16 const int md=1000000007;
    17 ll poww(ll a,ll b)
    18 {
    19     ll ans=1;
    20     for(;b;b>>=1,a=a*a%md)
    21         if(b&1)
    22             ans=ans*a%md;
    23     return ans;
    24 }
    25 int calc_phi(int n)
    26 {
    27     if(n<=100000)    return phi[n];
    28     int ans=n;
    29     for(int i=1;i<=len&&prime[i]*prime[i]<=n;++i)
    30         if(n%prime[i]==0)
    31         {
    32             ans-=ans/prime[i];
    33             while(n%prime[i]==0)    n/=prime[i];
    34         }
    35     if(n>1)    ans-=ans/n;
    36     return ans;
    37 }
    38 int calc(int x)
    39 {
    40     return poww(n,x)*calc_phi(n/x)%md;
    41 }
    42 int main()
    43 {
    44     int i,j;
    45     phi[1]=1;
    46     for(i=2;i<=100000;++i)
    47     {
    48         if(!nprime[i])    prime[++len]=i,phi[i]=i-1;
    49         for(j=1;j<=len&&i*prime[j]<=100000;++j)
    50         {
    51             nprime[i*prime[j]]=1;
    52             if(i%prime[j]==0)    {phi[i*prime[j]]=phi[i]*prime[j];break;}
    53             else    phi[i*prime[j]]=phi[i]*(prime[j]-1);
    54         }
    55     }
    56     scanf("%d",&T);
    57     while(T--)
    58     {
    59         ans=0;
    60         scanf("%d",&n);
    61         for(i=1;i*i<n;++i)
    62             if(n%i==0)
    63             {
    64                 ans+=calc(i);
    65                 if(ans>=md)    ans-=md;
    66                 ans+=calc(n/i);
    67                 if(ans>=md)    ans-=md;
    68             }
    69         if(i*i==n)
    70         {
    71             ans+=calc(i);
    72             if(ans>=md)    ans-=md;
    73         }
    74         printf("%lld
    ",ans*poww(n,md-2)%md);
    75     }
    76     return 0;
    77 }
    View Code

    upd20190305

    发现貌似不了解更完善的定义,在一些题目上会遇到奇怪的问题...

    证明?先咕咕咕

    来自wikipedia

    在数学中,群是由一个集合以及一个二元运算所组成的,符合下述四个性质(称为“群公理”)的代数结构。这四个性质是封闭性、结合律、单位元和对于集合中所有元素存在逆元素

    在集合论中,一个集合的置换是从该集合映至自身的双射

  • 相关阅读:
    2011/6/24 数据库分析
    项目代码总结
    背景透明 by sofish
    ie6 reflow bug
    ID与CLASS的使用技巧
    CSS浮动属性Float详解 by 帕兰
    javascript闭包 by 李松峰
    详解CSS选择器、优先级与匹配原理
    垂直对齐:verticalalign属性 by ddcatlee
    行高lineheight,以及基线、顶线、中线和底线,还有内容区域、行内框和行框 by 豆豆猫的窝
  • 原文地址:https://www.cnblogs.com/hehe54321/p/10467972.html
Copyright © 2011-2022 走看看