「專題總結」群論


萬事先吐槽:為什么我在這個專題瘋狂被卡常啊

群論這玩意是真的不接地氣。剛開始聽的時候這是個什么玩意啥也聽不懂啊。。。

然而其實有幾個概念,顯得很高端而已。(下面開始抄理解深刻的(他自己說的)$yxs$的博客

所謂的置換,其實就是把元素換位置。

置換群$G$就是一堆置換,滿足存在逆元和單位元(不動唄),有結合律,封閉性。。。

不動點就是某一個置換$i$中有多少個元素位置並沒有改變,稱為$c_i$。

$k$不動置換類就是所有使$k$位置元素位置不變的置換的集合,稱為$Z_k$

等價類就是元素$k$在任意置換后可能出現的位置集合,稱為$E_k$

根據含義可知等價類之間沒有交集,它們的並集是全集。等價類的數目稱為$L$

 

然后就有了燒邊$Burnside$引理:$L=\frac{\sum c_i}{|G|}$。就是不動點的平均值。

然而我不會也不想證明。。。這個東西沒什么擴展性,記結論差不多就夠了

如果哪天需要了,被打臉了就上去看群論之神的博客就好了。

以及還有$Burnside$的具體應用$Polya$。如果沒有元素個數等限制的話:

$L=\frac{\sum m^{h_i}}{|G|}$

其中$m$是可以染的顏色種數,$h_i$表示元素$i$所在的循環節個數,其實也就是總置換數除以循環節長度。

然后記住結論就可以做題了。

 

因為理解很不深刻所以做這些題非常艱難,然后還頹了不少題解。。。在$yxm$說是水水水的題目上。

只能說,和$CSP-S \ 390+$的所有大佬們顯然還是有雙重巨大的差距啊。。。

 

題目順序按照知識點的深淺排序,也是推薦的做題順序。大致是難度排序。

(如果做題順序反了的話,基本就要想我一樣一路頹題解下來了)

 

Cards:

$Description:$

小春現在很清閑,面對書桌上的N張牌,他決定給每張染色,目前小春只有3種顏色:紅色,藍色,綠色.他詢問Sun有多少種染色方案,Sun很快就給出了答案.進一步,小春要求染出Sr張紅色,Sb張藍色,Sg張絕色.他又詢問有多少種方案,Sun想了一下,又給出了正確答案. 最后小春發明了M種不同的洗牌法,這里他又問Sun有多少種不同的染色方案.兩種染色方法相同當且僅當其中一種可以通過任意的洗牌法(即可以使用多種洗牌法,而每種方法可以使用多次)洗成另一種.Sun發現這個問題有點難度,決定交給你,答案可能很大,只要求出答案除以P的余數(P為質數).$Max{Sr,Sb,Sg}<=20,m<=60,m+1<p<100,n=Sr+Sb+Sg$

輸入數據保證任意多次洗牌都可用這 m種洗牌法中的一種代替,且對每種洗牌法,都存在一種洗牌法使得能回到原狀態。

其實我覺得題還不錯,再看一遍又不會了。。。我還是有必要講一講的。

因為對總數有限制,所以不能$Polya$,所以選擇去燒個邊。

首先,題目中的洗牌法並不存在單位元,所以我們強制加入一種“洗牌法”是所有牌都不變。

數據范圍很小,對於每種洗牌法我們都可以暴力算出循環節大小與個數。

對於每個循環節進行$dp$,每個循環節都要染成相同的顏色。

然后就可以統計答案了。其實有點像個二維背包。

別忘了燒邊的時候最后答案要去掉置換群的大小,因為$p$是質數而且出題人仁慈的保證了$m+1<p$所以$exgcd$或快速冪亂寫都行。

 1 #include<cstdio>
 2 #define n (r+b+g)
 3 int pp,q[61][61],r,b,g,m,p,al[61],cir[61],cnt_cir,dp[61][21][21],ans,x,y,siz[61];
 4 void ex_gcd(int a,int b,int &x,int &y){
 5     if(!b){x=1;y=0;return;}
 6     ex_gcd(b,a%b,x,y);
 7     int res=x;x=y;y=res-a/b*x;
 8 }
 9 int main(){
10     scanf("%d%d%d%d%d",&r,&b,&g,&m,&p);
11     for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) scanf("%d",&q[i][j]);
12     for(int i=1;i<=n;++i) q[0][i]=i;
13     dp[0][0][0]=1;
14     for(int w=0;w<=m;++w){
15         cnt_cir=0;
16         for(int i=1;i<=n;++i)al[i]=0;
17         for(int i=1;i<=n;++i)if(!al[i]){
18             cnt_cir++; siz[cnt_cir]=1; pp=i; al[pp]=1;
19             while(!al[q[w][pp]]) al[q[w][pp]]=1, siz[cnt_cir]++, pp=q[w][pp];
20         }
21         for(int i=1;i<=cnt_cir;++i)for(int j=0;j<=r;++j)for(int k=0;k<=b;++k)dp[i][j][k]=0;
22         for(int i=1;i<=cnt_cir;++i)for(int j=r;j>=0;--j)for(int k=b;k>=0;--k){
23             (dp[i][j][k]+=dp[i-1][j][k])%=p;
24             if(j>=siz[i])(dp[i][j][k]+=dp[i-1][j-siz[i]][k])%=p;
25             if(k>=siz[i])(dp[i][j][k]+=dp[i-1][j][k-siz[i]])%=p;
26         }
27         (ans+=dp[cnt_cir][r][b])%=p;
28     }
29     ex_gcd(m+1,p,x,y);
30     x=(x%p+p)%p;
31     printf("%d\n",ans*x%p);
32 }
View Code

 

poj2154Color

$Description:$

Beads of N colors are connected together into a circular necklace of N beads (N<=1000000000). Your job is to calculate how many different kinds of the necklace can be produced. You should know that the necklace might not use up all the N colors, and the repetitions that are produced by rotation around the center of the circular necklace are all neglected.

You only need to output the answer module a given number PX <= 3500,1 <= P <= 30000

旋轉同構,翻轉不同構的環染色。模數是$int$的根號基本就代表。。。挺卡常的

非常符合$Polya$的形式,問題就在於統計循環節個數和對應的置換個數了。

置換有哪些?單位元,轉$\frac{1}{n}$圈,轉$\frac{2}{n}$圈。。。轉$\frac{n-1}{n}$圈。

對於每個置換,循環節長度就是$\frac{n}{gcd(i,n)}$。那么循環節個數就是$gcd(i,n)$。

但是你要做$n$次$gcd$?太慢 了,就算$O(1)$也不行啊。

$gcd$的值只可能是$n$的約數,這樣的數只有$\sqrt{n}$個,枚舉它們。

然后以此為$gcd$的值有$\varphi (n/i)$個。考慮歐拉函數的實際含義就可以理解。

歐拉函數只要根號硬篩就可以了。答案就是$\frac{\sum\limits_{i|n} \varphi(n/i) \times n^{i}}{n}$

然而除法懶得做的話可以直接在快速冪時指數-1偷個懶,因為這題恰好顏色數等於置換數。

這也導致我在頹$std$的時候懵逼了半天。

因為卡常不能define int long long,求歐拉函數最開始是先乘后除炸$int$,$WA$了$5$發,菜死了。

 1 #include<cstdio>
 2 int mod,n;
 3 int pow(int b,int t,int a=1){for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;}
 4 int phi(int x){
 5     int ans=x;
 6     for(int i=2;i*i<=x;++i)if(x%i==0){
 7         ans=ans/i*(i-1);
 8         while(x%i==0)x/=i;
 9     }return (x^1?ans/x*(x-1):ans)%mod;
10 }
11 signed main(){
12     int t,ans;scanf("%d",&t);
13     while(t--){
14         scanf("%d%d",&n,&mod);ans=0;
15         for(int i=1;i*i<=n;++i)if(n%i==0){
16             ans=(ans+pow(n%mod,n/i-1)*phi(i))%mod;
17             if(i*i^n)ans=(ans+pow(n%mod,i-1)*phi(n/i))%mod;
18         }
19         printf("%d\n",ans);
20     }
21 }
View Code

 

poj2888Magic Bracelet:

$Description:$

Ginny’s birthday is coming soon. Harry Potter is preparing a birthday present for his new girlfriend. The present is a magic bracelet which consists of n magic beads. The are m kinds of different magic beads. Each kind of beads has its unique characteristic. Stringing many beads together a beautiful circular magic bracelet will be made. As Harry Potter’s friend Hermione has pointed out, beads of certain pairs of kinds will interact with each other and explode, Harry Potter must be very careful to make sure that beads of these pairs are not stringed next to each other.

There infinite beads of each kind. How many different bracelets can Harry make if repetitions produced by rotation around the center of the bracelet are neglected? Find the answer taken modulo 9973

$1 ≤ n ≤ 10^9, gcd(n, 9973) = 1, 1 ≤ m ≤ 10,1 ≤ k ≤ m(m - 1)- 2)$

與上一題不同的在於,這道題對相鄰元素有限制了。

先不考慮它是一個環,如果就是普通這樣的限制讓你做一條鏈,不難想到簡單$dp$

但是復雜度不對$n$太大了,但是發現$m$小的出奇,於是可以用矩陣快速冪優化。

現在考慮,鏈變成環了怎么辦?其實要求就是第1個元素和第n個元素也要滿足限制。

斷環成鏈的話就變成了第$n$個元素和第$n+1$個元素滿足限制,同時第$1$和第$n+1$個元素相同就可以了。

這樣只要記錄下第一個元素是什么,然后做一個長度為$n+1$的鏈$dp$(就是轉移$n$次)就可行了。

這樣你就統計出了合法的置換類有多少種,燒個邊弄個歐拉函數,和上一題就一樣了。

模數在$int$的根號內。。。還被卡常。。。poj真無良。

 1 #include<cstdio>
 2 #define mod 9973
 3 int pow(int b,int t,int a=1){b%=mod;for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;}
 4 int A[11][11],B[11][11],R[11][11],C[11][11],m;
 5 int phi(int x){int ans=x;
 6     for(int i=2;i*i<=x;++i)if(x%i==0){ans=ans/i*(i-1);while(x%i==0)x/=i;}
 7     return (x==1?ans:ans/x*(x-1))%mod;
 8 }
 9 void mul(){
10     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)for(int k=1;k<=m;++k)C[i][j]=(C[i][j]+B[i][k]*B[k][j])%mod;
11     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)B[i][j]=C[i][j],C[i][j]=0;
12 }
13 void Mul(){
14     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)for(int k=1;k<=m;++k)C[i][j]=(C[i][j]+A[i][k]*B[k][j])%mod;
15     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)A[i][j]=C[i][j],C[i][j]=0;
16 }
17 int ask(int n){int ans=0;
18     for(int j=1;j<=m;++j)for(int k=1;k<=m;++k)B[j][k]=R[j][k];
19     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)A[i][j]=0;
20     for(int i=1;i<=m;++i)A[i][i]=1;
21     for(int t=n;t;t>>=1,mul())if(t&1)Mul();
22     for(int i=1;i<=m;++i)ans=(ans+A[i][i])%mod;
23     return ans;
24 }
25 int main(){
26     int T;scanf("%d",&T);while(T--){
27         int n,k,ans=0;scanf("%d%d%d",&n,&m,&k);
28         for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)R[i][j]=1;
29         for(int i=1,x,y;i<=k;++i)scanf("%d%d",&x,&y),R[x][y]=R[y][x]=0;
30         for(int i=1;i*i<=n;++i)if(n%i==0){
31             ans=(ans+phi(i)*ask(n/i))%mod;
32             if(i*i!=n)ans=(ans+phi(n/i)*ask(i))%mod;
33         }printf("%d\n",ans*pow(n,mod-2)%mod);
34     }
35 }
View Code

 

周末晚會:

$Description:$

Irena和Sirup正准備下個周末的Party。為這個Party,他們剛剛買了一個非常大的圓桌。他們想邀請每個人,但他們現在不知道如何分配座次。Irena說當有超過K個女孩座位相鄰(即這些女孩的座位是連續的,中間沒有男孩)的話,她們就會說一整晚的話而不和其他人聊天。 Sirup沒有其他選擇,只有同意她。然而,作為一名數學家,他很快地痴迷於所有可能方案。 題目說明: N個人圍繞着圓桌坐着,其中一些是男孩,另一些是女孩。你的任務是找出所有合法的方案數,使得不超過K個女孩座位是連續的。 循環同構會被認為是同一種方案。對於100%的數據N,K < = 2000;mod 100000007

限制條件變了,不再是相鄰,而是不能連續超過$k$。

發現這次$n,k$范圍都不大,所以可以做$O(nk)$的$dp$

然后還要再容斥一下,把首尾接起來超過$k$的部分去掉。

只要強制限制頭上有連續幾個女生和1個男生,剩下的正常選就可以。

然后可以前綴和優化,但是最好不要。。。

不然就會像我一樣被卡常,$T$它個$5$發才爽。。。

然后就又和上一道題一樣了。

還有這道題的模數,它比平時少個0!少了一個0!!!出題人真是吃多了。。。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 100000007
 4 int dp[2222][2222],sum[2222][2222],k,n;
 5 int pow(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
 6 int Mod(int x){return x>=mod?x-mod:x;}
 7 int phi(int x){int ans=x;
 8     for(int i=2;i*i<=x;++i)if(x%i==0){
 9         ans=ans/i*(i-1);
10         while(x%i==0)x/=i;
11     }return x^1?ans/x*(x-1):ans;
12 }
13 int cal(int x,int ans=0){
14     for(int i=0;i<=k;++i)ans=Mod(ans+dp[x][i]);
15     for(int i=1;i<=k;++i)ans=Mod(ans-Mod(sum[max(x-k-2+i,0)][i]-sum[max(0,x-k-2)][i]+mod)+mod);
16     if(x<=k&&k<n)ans--;
17     return ans;
18 }
19 int main(){dp[0][0]=1;
20     int T;scanf("%d",&T);while(T--){
21         int ans=0;scanf("%d%d",&n,&k);
22         for(int i=1;i<=n;++i)for(int j=0;j<=k&&j<=i+1;++j)dp[i][j]=0;
23         for(int i=0;i<n;++i){
24             for(int j=0;j<=k&&j<=i+1;++j)dp[i+1][0]=Mod(dp[i+1][0]+dp[i][j]);
25             for(int j=0;j<k&&j<=i+1;++j)dp[i+1][j+1]=Mod(dp[i+1][j+1]+dp[i][j]);
26         }
27         for(int i=0;i<n;++i)sum[0][i]=dp[0][i];
28         for(int i=0;i<n;++i)for(int j=1;j<n;++j)sum[j][i]=Mod(sum[j-1][i]+dp[j][i]);
29         for(int i=1;i*i<=n;++i)if(n%i==0){
30             ans=(ans+1ll*phi(i)*cal(n/i))%mod;
31             if(i*i!=n)ans=(ans+1ll*phi(n/i)*cal(i))%mod;
32         }
33         printf("%lld\n",1ll*pow(n,mod-2)*ans%mod);
34     }
35 }
View Code

 

color 有色圖:

$Description:$

如果一張無向完全圖(完全圖就是任意兩個不同的頂點之間有且僅有一條邊相連)的每條邊都被染成了一種顏色,我們就稱這種圖為有色圖。如果兩張有色圖有相同數量的頂點,而且經過某種頂點編號的重排,能夠使得兩張圖對應的邊的顏色是一樣的,我們就稱這兩張有色圖是同構的。以下兩張圖就是同構的,因為假如你把第一張圖的頂點(1,2,3,4)置換成第二張圖的(4,3,2,1),就會發現它們是一樣的。

你的任務是,對於計算所有頂點數為n,顏色種類不超過m的圖,最多有幾張是兩兩不同構的圖。

由於最后的答案會很大,你只要輸出結論模p的余數就可以了(p是一個質數)$1≤n≤53,1≤m≤1000,n<p≤10^9$

這是真大神題了。。。啃題解才能明白。。。

要考慮好,這道題的置換的元素是什么?並不是點,而是邊。點是對於邊而言的置換。

(然而並不是每個點是一個置換,因為圖是有標號的,實際上每個點的排列就是一個置換)

所以置換的全種類有$53!$種。太大了。

我們先從其它方面入手。因為點是邊的置換,所以有一些點可以構成循環。

然后我們再考慮所有邊就只分為了兩類:兩個端點是否在同一個循環內。

如果它在兩個循環內,循環的點數分別為$x$和$y$的話,那么循環節長度就是$lcm(x,y)$。這樣的話所有點都回到原點了。

這類的元素數(也就是邊數)一共有$xy$個。所以說這樣產生的循環節個數就是$\frac{xy}{lcm(x,y)}=gcd(x,y)$了。

(弱智了,總元素等於循環節長度乘循環節個數。原來寫的不動點個數。感謝青君大神指出。下文同)

如果邊的兩段在同一個循環內,循環的點數為$x$的話,那么這樣的邊有$C_x^2$個。

循環節長度當然是$x$,所以循環節個數是$\frac{C_x^2}{x}=\frac{x-1}{2}$

然而這樣並沒有考慮到所有情況。因為邊是雙向的,所以,在點數為偶數時,邊翻轉后是一樣的,比上面的情況多一個類似於翻轉同構的循環形式。

所以這時候循環節個數為$\frac{x}{2}$。

綜上所述,設某個循環$i$中的點數為$p_i$,那么總循環節個數就是$\sum\limits_{i=1}^{cnt} \lfloor \frac{p_i}{2} \rfloor + \sum\limits_{i=1}^{cnt} \sum\limits_{j=i+1}^{cnt} gcd(p_i,p_j)$

現在,對於一個給定循環的局面,我們已經可以求出它的循環節個數了。

於是就可以利用$Polya$定理的形式,知道$m^{c}$就是總的等價類數目。

現在我們只需要求出所有局面的和。

總的局面肯定是$n!$個了,這$n$個點可以隨意排列在循環中的位置。

但是你枚舉每一種局面總數是$n!$自然不可接受。我們只枚舉本質不同的就好了,所謂本質不同,就是至少有一個循環的大小不一致。

這樣的狀態數還是有點多,我們強制循環的大小有序,必須從小到大,這樣就可以開始愉快的搜索了。

因為$n$並不大只有$53$(這數據范圍看着不像搜索剪枝嘛?),它的正整數拆分方案數並不多,所以搜就完了。

最后一個問題就是,我們現在強制有序還本質不同,問題在於這種局面到底出現了多少次。

我們把點拍到序列上,對於所有點進行全排列,每個循環內部的排列順序是循環同構的,所以要除掉$p_i$

即$1234$和$2341$一樣,但是和$1324$就不一樣。

而且,因為保證了循環有序,大小相同的循環其實被規定了先后順序,所以對於所有$i$如果有$s_i$個循環的大小都是$i$那么方案數就要除掉$i!$

所以總的方案就是$\frac{n!}{\prod (s_i!) \prod p_i}$

對於每種划分,答案與不動點個數相乘計入總數,最后還是別忘了除掉總的置換群大小$n!$。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define int long long
 4 int mod,n,m,fac[66],inv[66],ans,s[66],I[66];
 5 int pow(int b,int t,int a=1){for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;}
 6 int gcd(int x,int y){return y?gcd(y,x%y):x;}
 7 void cal(int t){
 8     int tot=0,ctb;
 9     for(int i=1;i<=t;++i)tot+=s[i]>>1;
10     for(int i=1;i<=t;++i)for(int j=i+1;j<=t;++j)tot+=gcd(s[i],s[j]);
11     ctb=pow(m,tot)*fac[n]%mod;
12     for(int i=1;i<=t;++i)ctb=ctb*I[s[i]]%mod;
13     for(int i=1,l=1;i<=t+1;++i)if(s[i]!=s[i-1])ctb=ctb*inv[i-l]%mod,l=i;
14     ans=(ans+ctb)%mod;
15 }
16 void sch(int lft,int lst,int t){
17     if(!lft)s[t+1]=0,cal(t);
18     for(int i=lst;i<=lft;++i)s[t+1]=i,sch(lft-i,i,t+1);
19 }
20 main(){
21     scanf("%lld%lld%lld",&n,&m,&mod); fac[0]=inv[0]=1;
22     for(int i=1;i<=n;++i)inv[i]=pow(fac[i]=fac[i-1]*i%mod,mod-2),I[i]=pow(i,mod-2);
23     sch(n,1,0);cout<<ans*inv[n]%mod<<endl;
24 }
思維極度復雜,代碼極端好寫


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM