【Pólya计数】hnoi2009图的同构计数

题目的简单描述:计算出含有n(n<60)个点的无向图的本质不同的个数。

这里面本质不同的概念是一个图通过改变一些点的序号可以变成其他的图。

 

刚开始看这道题是晕的,60的范围很让人蛋疼,搜索?DP?记得以前在LTC男人八题里有一道有N个点的连通图的个数,但是这道题所有点是一样的,就涉及到了同构的问题。是通过非常巧妙的状态DP?还是通过等价类的搜索?

 

后来问了道长是Pólya计数。。比较无语的是,我看见这道题第一个排除的算法就是Pólya,他说的:任意两个点之间是没有区别的,那么也就意味着置换群有N!种,统计就更不现实的。

 

但是,实际上在这N!种置换群种有很多的同类的,一个比较直观的想法就是:对于每个循环节长度(的最小表示)完全一致的一起统计。

但是图同构是基于边(边的选与不选),所以我们还要把点置换转换为边置换,因为我们知道了点置换以后边置换就很明确了。

 

每个点置换的循环节长度怎么办?搜!其实实际上我们相当于求了一个N的整数拆分,60的整数拆分是多少?1000000!

 

那么现在我们的任务就是统计同类的置换有多少种,因为我们已经枚举除了每个点置换的循环节长度,那么剩下的任务就是把这些点‘放入’这些‘桶子’之中。但是由于是无向图,我们还需要注意一些细节。。。

 

关于除法运算,我们使用欧拉定理即可(题目要求输出ans mod 997,997为质数):a/b=a*bp-2 (mod p).

另外,做充分的预处理是非常重要的,要不然就只能打表交了。。。

 

program ex2;const mo=997; var g:array[0..99,0..99] of longint; e,a,b,c,d:array[0..5000] of longint; n,i,j,k,ans,zz:longint; function gcd(a,b:longint):longint; begin if b=0 then gcd:=a else gcd:=gcd(b,a mod b) end; procedure search(t,s,p,f,l:longint);var i,j,k:longint; begin i:=a[t-1];if s<i then i:=s; if i=0 then ans:=(ans+e[p]*f*d[l])mod mo else for i:=i downto 1 do begin a[t]:=i;k:=p+i>>1; for j:=1 to t-1 do inc(k,g[a[j],i]); if a[t-1]=i then search(t+1,s-i,k,f*b[i] mod mo,l+1) else search(t+1,s-i,k,f*b[i]*d[l] mod mo,1) end; end; begin assign(input,'input.txt');reset(input); assign(output,'output.txt');rewrite(output); readln(n); for i:=1 to n do for j:=1 to n do g[i,j]:=gcd(i,j); e[0]:=1;c[0]:=1;a[0]:=n+1; for i:=1 to n do c[i]:=c[i-1]*i mod mo; for i:=1 to 5000 do e[i]:=e[i-1]*2 mod mo; for i:=1 to n do begin b[i]:=1; for j:=1 to mo-2 do b[i]:=b[i]*i mod mo; end; for i:=1 to n do begin d[i]:=1; for j:=1 to mo-2 do d[i]:=d[i]*c[i] mod mo; end; search(1,n,0,c[n],1); writeln(ans*d[n] mod mo); close(input);close(output); end.

你可能感兴趣的:(c,算法,search,input,任务,output)