SGU 208 Toral Tickets(Polya计数+Java大数)

来源:互联网 发布:淘宝没有访客怎么办 编辑:程序博客网 时间:2024/06/08 18:42

208. Toral Tickets

time limit per test: 0.25 sec.
memory limit per test: 65536 KB
input: standard
output: standard

On the planet Eisiem passenger tickets for the new mean of transportation are planned to have the form of tores. 
Each tore is made of a single rectangular black rubber sheet containing N × M squares. Several squares are marked with white, thus encoding the ticket's source and destination. 
When the passenger buys the ticket, the ticket booking machine takes the rubber sheet, marks some squares to identify the route of the passenger, and then provides it to the passenger. The passenger next must glue the ticket.
The ticket must be clued the following way. First two its sides of greater length are glued together, forming a cylinder. Next cylinder base circles, each of which has the length equal to the length of the short side of the original rubber sheet, are glued together. They must be glued in such a way, that the cells, sides of which are glued, first belonged to the same row of the sheet. Note that the inner and the outer part of the sheet can be distinguished.
The resulting tore is the valid ticket.
Note that if the original sheet is square, there are two topologically different ways to make a tore out of a rubber sheet.
Ticket material is so perfect and gluing quality is so fine, that no one is able to find the seam, and this leads to some problems. First, the same tore can be obtained using different sheets. More of that, the same sheet can lead to tores that look a bit different.
Now the transport companies of Eisiem wonder, how many different routes they can organize, so that the following conditions are satisfied:
  • tickets for different routes are represented by different tores;
  • if some rubber sheet was marked to make the tore for some route, it cannot be used to make the tore for another route.


Help them to calculate the number of routes they can organize.
Input
The first line of the input file contains N and M (1 ≤ N, M ≤ 20).
Output
Output the number of routes Eisiem transport companies can organize.
Sample test(s)
Input
Test #1 2 2 Test #2 2 3
Output
Test #1
6
Test #2
13



        ICPC青岛站的E题,非常明显的一道polya,当场秒看出来,但是无奈不会求置换,或者说没做过类似的题。

        看着别人好多都过了,还是有点不甘心的。于是回来专门找了找这种题目去做。

        首先,我们来回顾一下polya定理和burnside引理。其实,本质上来说,之前做的所有题都是利用burnside引理,而这个polya定理就是我没有见过的,也正是赛场上出现的题目。burnside引理:对于一个置换f,若一个着色方案s经过置换后不变,称s为f的不动点。将f的不动点数目记为Z(f),则可以证明等价类数目为所有Z(f)的平均值。而polya定理:对于一个置换f,其所包含的循环数为C(f),则可以证明等价类数目为所有C(f)的平均值。两个定理其实是对同一个问题的不同的表示或者说解答方法。

        现在我们来看这一道题,由于格子数目比较多,旋转和置换又找不出明显的规律,所以说用burnside引理可能不太方便,因此考虑用polya定理。使用polya定理的话,可以总结为一下三个套路步骤:

        ①暴力搜出所有置换;

        ②搜出所有置换的循环;

        ③把答案累加后除以置换数。

        仔细解读一下,这种方法就是用纯理论方法表示,开一个数组表示置换,初始状态为初始置换,再由初始置换经过基本变换搜索拓展出所有的置换。具体到本题,可以分成两种情况。首先是N==M的时候,可以看作是一个正方形,所以说我们可以旋转0、90、180、270 度,然后还可以平移,平移的话就是最多移动M*N次,总的来说就有M*N*4种置换。然后对于N!=M的情况,可以看作是一个长方形,只能旋转0、180 度,然后同样可以平移M*N次,总的就有M*N*2种置换。然后由于本题的平移置换比较有规律可循,所以说直接用了循环的形式来枚举出置换,但是对于一般情况,我们需要用bfs拓展置换,并且用hash来判定重复。

        本题数据较大,同时也没要求取模,所以用上了Java大整数,顺便巩固新学的Java语言。具体见代码:

import java.math.*;import java.util.*;public class Solution {public static BigInteger two=BigInteger.valueOf(2);public static BigInteger p[]=new BigInteger[5000];public static int nxt[]=new int[30*30];public static int n,m;public static void init(){p[0]=BigInteger.ONE;for(int i=1;i<5000;i++)p[i]=p[i-1].multiply(two);}public static int cal()//统计循环个数{int res=0;boolean v[]=new boolean[30*30];for(int i=1;i<=n*m;i++){if (!v[i]){for(int j=i;!v[j];j=nxt[j]) v[j]=true;res++;}}return res;}public static void rotate()//旋转{int nex[]=new int[30*30];for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)nex[(m-j)*n+i]=nxt[(i-1)*m+j];int a=n; n=m; m=a;System.arraycopy(nex,0,nxt,0,nex.length);}public static void shift_right()//右移{int nex[]=new int[30*30];for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)nex[(i-1)*m+j%m+1]=nxt[(i-1)*m+j];System.arraycopy(nex,0,nxt,0,nex.length);}public static void shift_down()//下移{int nex[]=new int[30*30];for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)nex[(i%n)*m+j]=nxt[(i-1)*m+j];System.arraycopy(nex,0,nxt,0,nex.length);}public static void main(String[] args){init();Scanner cin=new Scanner(System.in);while(cin.hasNext()){n=cin.nextInt();m=cin.nextInt();boolean is_square=(n==m);BigInteger ans=BigInteger.ZERO;for(int i=1;i<=n*m;i++) nxt[i]=i;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){ans=ans.add(p[cal()]);//统计当前置换循环数rotate();//旋转得到新的置换if (is_square) ans=ans.add(p[cal()]);//如果是正方形,那么90°时也要计算rotate();ans=ans.add(p[cal()]);rotate();if (is_square) ans=ans.add(p[cal()]);rotate();shift_right();//向右平移}shift_down();//向下平移}BigInteger f=BigInteger.valueOf(n*m*2);if (is_square) f=f.multiply(two);System.out.println(ans.divide(f));//最后结果除以置换数}}}