[HDU1812]置换群 Polya定理 等价涂色问题详解

Count the Tetris

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1840    Accepted Submission(s): 497


Problem Description
话说就是因为这个游戏,Lele已经变成一个名人,每当他一出现在公共场合,就有无数人找他签名,挑战。

为了防止引起社会的骚动,Lele决定还是乖乖呆在家里。

在家很无聊,Lele可不想像其他人一样每天没事在家数钱玩,于是他就开始数棋盘。他想知道,一个有N×N个格子的正方形棋盘,每个格子可以用C种不同颜色来染色,一共可以得到多少种不同的棋盘。如果一个棋盘,经过任意旋转,反射后变成另一个棋盘,这两个棋盘就是属于同一种棋盘。

比如当N=C=2的时候,有下面六种不同的棋盘



现在告诉你N和C,请你帮帮Lele算算,到底有多少种不同的棋盘
 

Input
本题目包含多组测试,请处理到文件结束。
每组测试数据包含两个正整数N和C(0<N,C,<31),分别表示棋盘的大小是N×N,用C种颜色来进行染色。
 

Output
对于每组测试,在一行里输出答案。
 

Sample Input
  
  
2 2 3 1
 

Sample Output
  
  
6 1
 

Author
linle

标准的polya定理问题。

旋转只有 0,90,180,270度三种旋法。
旋0度,则置换的轮换数为n*n
旋90度,n为偶数时,则置换的轮换数为n*n/4,n为奇数,则置换的轮换数为(n*n-1)/4+1
旋180度,n为偶数时,则置换的轮换数为n*n/2,n为奇数,则置换的轮换数为(n*n-1)/2+1
旋270度,n为偶数时,则置换的轮换数为n*n/4,n为奇数,则置换的轮换数为(n*n-1)/4+1

反射 沿对角反射两种,沿对边中点连线反射两种
n为偶数时,沿对边中点连线反射两种的置换轮换数为 n*n/2
                     沿对角反射两种的置换轮换数为 (n*n-n)/2+n
n为奇数时,沿对边中点连线反射两种的置换轮换数为 (n*n-n)/2+n
                     沿对角反射两种的置换轮换数为 (n*n-n)/2+n



关于涂色问题:

 


  



更详细的解题报告:

HDOJ 1812解题报告

 

题目描述:

       一个有N×N个格子的正方形棋盘,每个格子可以用C种不同颜色来染色,一共可以得到多少种不同的棋盘。如果一个棋盘,经过任意旋转,翻转后变成另一个棋盘,这两个棋盘就是属于同一种棋盘。(0<N,C,<31)

 

分析:

       很明显这个题目要用polya定理,和经典的着色计数问题不同的是经过翻转得到的棋盘和原来的棋盘也是属于同一种棋盘的。其实如果你那个问题的推导理解了,这个是一样的。


       根据Polya定理的公式:

                     (m表示m种颜色)

 

首先,我们要确定置换群,很明显只有两种置换:旋转和翻转。

       旋转分为:转0°、转90°、转180°、转270°;

       翻转分为:水平翻转和垂直翻转;

       看上去好像是有12种组合,其实不知大家有没注意到转180°再旋转和转0°再旋转是一样的,同样,转270°再旋转和转90°再旋转是一样的。

       所以,置换群G={转0°,转90°,转180°,转270°,转0°再水平翻转,转0°垂直翻转,转90°再水平翻转,转90°垂直翻转},|G|=8。

 

然后,再考虑每种情况的循环节数:

       N为偶数时:

 1

 2

 4

 3

             

       转0°:(1,2,3,4)=(1)(2)(3)(4)           循环个数:4    n*n

       转90°:(4,1,2,3)=(1,4,3,2)              循环个数:1 n*n/4

       转180°:(3,4,1,2)=(1,3)(2,4)           循环个数:2 n*n/2

       转270°:(2,3,4,1)=(1,2,3,4)            循环个数:1 n*n/4

       转0°再水平翻转:(2,1,4,3)=(1,2)(3,4)  循环个数:2  n*n/2

       转0°再垂直翻转:(4,3,2,1)=(1,4)(2,3)  循环个数:2  n*n/2

       转90°再水平翻转(相当于以对角线为轴反转):(1,4,3,2)=(1)(2,4)(3)循环个数:3  (n*n+n)/2

       转90°再垂直翻转(同上):(3,2,1,4)=(1,3)(2)(4)循环个数:3  (n*n+n)/2

(关于公式的推导,可以多思考分析一下,多画几个出来可能就能够看出来了,如果还有问题可以来跟我讨论下,这里就不再证明推导了。)

       同样的道理可以推出奇数的情况。

       转0°:n*n

       转90°:(n*n+3)/4

       转180°:(n*n+1)/2

       转270°:(n*n+3)/4

       转0°再水平翻转:n*n/2+n

       转0°再垂直翻转:n*n/2+n

       转90°再水平翻转:(n*n+n)/2

       转90°再垂直翻转:(n*n+n)/2

 

最后,再带入polya定理的公式,再考虑下题目数据,格式方面有没有什么特殊的地方,这个问题就解决了。比如说这个题目,因为0<N,C,<31,故要用大数处理。还有这个题目我当时比赛的时候直接计算而且用二分求幂优化,结果还是TLE了很多次,所以建议做这个题目时要预处理一下,先把所有结果都计算并保存。

 

这三步也是用polya定理解题目的一般思路。



<pre name="code" class="java">import java.io.*;
import java.util.Scanner;
import java.math.BigInteger;
class Main
{
    public static void main(String[]Args) throws FileNotFoundException
    {
            InputStream in=System.in;
            Scanner cin = new Scanner(in);

            int n,t;
            BigInteger c,sum,x,two=BigInteger.valueOf(2);
            BigInteger four=BigInteger.valueOf(4);
            BigInteger eight=BigInteger.valueOf(8);
            while(cin.hasNext())
            {
                n=cin.nextInt();
                c=cin.nextBigInteger();
                sum=BigInteger.ZERO;
                if(n%2==0)
                {
                    x=c.pow(n*n);
                    sum=sum.add(x);
                    x=two.multiply(c.pow(n*n/4));
                    sum=sum.add(x);
                    x=c.pow(n*n/2);
                    sum=sum.add(x);
                    x=two.multiply(c.pow((n*n-n)/2+n));
                    sum=sum.add(x);
                    x=two.multiply(c.pow(n*n/2));
                    sum=sum.add(x);
                    sum=sum.divide(eight);
                }
                else
                {
                    x=c.pow(n*n);
                    sum=sum.add(x);
                    x=two.multiply(c.pow((n*n-1)/4+1));
                    sum=sum.add(x);
                    x=c.pow((n*n-1)/2+1);
                    sum=sum.add(x);
                    x=four.multiply(c.pow((n*n+n)/2));
                    sum=sum.add(x);
                    sum=sum.divide(eight);
                }
                
                System.out.println(sum);
            }
    }
}


 
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值