前言
計數題
題目相關
題目大意
個點的完全圖,對邊染色(顏色有種),求本質不同的染色方案數,答案對取模
數據範圍
題解
我們乍一看是染色問題,我們就想到了Polya定理
我們可以用其式子來計算
但是我們發現染色的是邊而不是點,而是邊,所以我們計算的置換是對於邊的
我們考慮先不枚舉邊置換,而是枚舉點的置換,容易發現,一個點置換,其對應的邊置換是唯一確定的
這樣一來,就有了方便的枚舉置換方式
我們發現,點的置換有種,我們發現的複雜度顯然不能接受
我們考慮不直接枚舉所有點置換,而是枚舉本質不同的點置換,本質相同的點置換,對應的邊置換也是本質相同的
這樣的話複雜度就是的整數劃分了,隨便寫個程序就可以發現當時,整數劃分是級別的,目前看起來可以接受
code
#include<cstdio>
#include<cctype>
#define rg register
template<typename T>inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template<typename T>inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template<typename T>inline void print(const T x){if(x>=0)printe(x);else putchar('-'),printe(-x);}
int n,ans;
void dfs(const int t,const int h)
{
if(t==0)ans++;
else for(rg int i=h;i<=t;i++)dfs(t-i,i);
}
int main()
{
read(n);
dfs(n,1);
print(ans);
return 0;
}
我們考慮現在得到了一個整數劃分,其有個循環,按循環長度排序後的第個循環長度爲即
我們思考對於這樣一個點置換,其對應的邊置換的循環數量(在Polya中要求的東西)
兩個端點在同一個長度爲的點循環內的情況:
我們發現,對於任意一條邊,將其兩邊的點轉動,這樣就可以產生一個大小爲的邊循環
然後這個時候有個特例,當爲偶數時,有一個大小爲的循環,即環上每組相對的點所組成的循環
即:
爲奇數時,循環數爲
爲偶數時,循環數爲
綜上,這裏的邊循環數爲
兩個端點分別在長度爲、的點循環內的情況:
容易發現,這裏的邊循環長度爲
那麼循環數量爲
這樣的話,對於一個枚舉到的本質不同的點循環,其對Poyla定理中後面那個sigma的指數就爲
我們也知道顏色數,現在還需要統計這種點循環的出現次數
容易發現,出現次數可以直接計算
我們設爲長度爲的出現的次數(另外,定義)
這樣的話我們就能快速的求出
最終答案就可以算出來了
代碼
這份代碼爲了方便理解,寫的複雜度比較大,實際可以有更好的寫法使程序跑得更快
#include<cstdio>
#include<cctype>
#define rg register
typedef long long ll;
template<typename T>inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template<typename T>inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template<typename T>inline void print(const T x){if(x>=0)printe(x);else putchar('-'),printe(-x);}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
ll n,m,p,L[61],fac[61],ans,tim[61];
inline ll pow(ll x,ll y)
{
ll res=1;
for(;y;y>>=1,x=x*x%p)if(y&1)res=res*x%p;
return res;
}
void dfs(const int step,const int t,const int h)
{
if(t==0)
{
ll xs=0,xf=1;
for(rg int i=1;i<step;i++)
{
xs+=L[i]>>1;
for(rg int j=i+1;j<step;j++)xs+=gcd(L[i],L[j]);
xf=xf*L[i]%p;
}
for(rg int i=1;i<=n;i++)xf=xf*fac[tim[i]]%p;
ans+=pow(m,xs)*pow(xf,p-2)%p;
}
else for(rg int i=h;i<=t;i++)
{
L[step]=i;
tim[i]++;
dfs(step+1,t-i,i);
tim[i]--;
}
}
int main()
{
read(n),read(m),read(p);
fac[0]=1;for(rg int i=1;i<=60;i++)fac[i]=fac[i-1]*i%p;
dfs(1,n,1);
print(ans%p);
return 0;
}
總結
代碼非常的清真,良好的計數技巧配上Poyla定理就能解決這題
計數好難