OI群论:从入门到自闭
怎么這么多閱讀啊……
這篇文章是群論(其實只有Polya)在信息學奧賽中計數的運用,并不是群論的講義……
群論可以說是數學中最高深的內容,一個oier去深入了解是不現實的。因此,我們只需要知道結論。
本文主要講解群論在oi中輔助計數的運用,將盡量做到通俗易懂。
Luogu P4980
題意:給一個長度為NNN的環,染NNN種色,旋轉同構,求方案數
N≤1e9N \leq 1e9N≤1e9
基本概念
置換 一個排列ppp,表示iii可以變成pip_ipi?
本題中,旋轉后得到的排列就是置換。
(1,2,3,...,n)(2,3,4,...,n,1)......(n,1,2,...,n?1)(1,2,3,...,n)(2,3,4,...,n,1)... ...(n,1,2,...,n-1)(1,2,3,...,n)(2,3,4,...,n,1)......(n,1,2,...,n?1)都是置換
置換群一堆置換
置換間可以相乘。規則是對每個iii依次進行每個置換
本題中,上述nnn個置換構成置換群
軌道某個數aaa,經過若干置換(可以為1)回到自己,經過的數在一個軌道上。
人話:環
本題中,若n=6n=6n=6,對于置換(3,4,5,6,1,2)(3,4,5,6,1,2)(3,4,5,6,1,2),3?>5?>13->5->13?>5?>1和4?>6?>24->6->24?>6?>2是兩個軌道
Burnside引理
本質不同的方案數等于每個置換跑了之后不變化的方案數的平均值
人話:對于每個置換,把每個軌道縮成點,求出當前方案數,所有方案的和除以總置換數就是本質不同的方案數。
這樣可以枚舉置換,然后暴力跑出置換數,可以做到O(n2)O(n^2)O(n2)
然而這題有個特殊性質,容(bu)易(yong)證明
軌道數=gcd(旋轉次數,n)\text{軌道數=gcd(旋轉次數,n)}軌道數=gcd(旋轉次數,n)
這樣可以得到公式:
Ans=1n∑i=1nngcd(i,n)Ans=\frac{1}{n}\sum_{i=1}^n n^{gcd(i,n)}Ans=n1?∑i=1n?ngcd(i,n)
如果我沒理解錯,這個就是polyapolyapolya定理
真心不知道有啥區別
可以做到O(nlogn)O(nlogn)O(nlogn)
優化
回到
Ans=1n∑i=1nngcd(i,n)Ans=\frac{1}{n}\sum_{i=1}^n n^{gcd(i,n)}Ans=n1?∑i=1n?ngcd(i,n)
我們發現gcd(i,n)gcd(i,n)gcd(i,n) 只有O(n)O(\sqrt{n})O(n?)種取值
可以枚舉gcd(i,n)gcd(i,n)gcd(i,n)
Ans=1n∑d∣n∑i=1nd[gcd(i,nd)=1]ndAns=\frac{1}{n}\sum_{d|n}\sum_{i=1}^{\frac{n}ze8trgl8bvbq}[gcd(i,\frac{n}ze8trgl8bvbq)=1]n^dAns=n1?∑d∣n?∑i=1dn??[gcd(i,dn?)=1]nd
不就是歐拉函數嗎
Ans=1n∑d∣n?(nd)nd=∑d∣n?(nd)nd?1Ans=\frac{1}{n}\sum_{d|n}\phi(\frac{n}ze8trgl8bvbq)n^d=\sum_{d|n}\phi(\frac{n}ze8trgl8bvbq)n^{d-1}Ans=n1?∑d∣n??(dn?)nd=∑d∣n??(dn?)nd?1
然后是暴力枚舉ddd,暴力算?\phi?,復雜度O(n34)O(n^{\frac{3}{4}})O(n43?)
并不會證明
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> using namespace std; typedef long long ll; const int MOD=1e9+7; inline int qpow(int a,int p) {int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD,p>>=1;}return ans; } inline int phi(int x) {int ans=x;for (int i=2;i*i<=x;i++)if (x%i==0){ans/=i,ans*=i-1;while (x%i==0) x/=i;}if (x>1) ans/=x,ans*=x-1;return ans; } int n; int calc(const int&d){return (ll)phi(n/d)*qpow(n,d-1)%MOD;} int main() {int T;scanf("%d",&T);while (T--){int ans=0;scanf("%d",&n);for (int i=1;i*i<=n;i++)if (n%i==0) {ans=(ans+calc(i))%MOD;if (i*i<n) ans=(ans+calc(n/i))%MOD;}printf("%d\n",ans);}return 0; }總結
以上是生活随笔為你收集整理的OI群论:从入门到自闭的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手游王者荣耀音乐王子高渐离
- 下一篇: Steam如何申诉?Steam申诉流程图