HDU 3441 Rotation(Polya计数)

题意:将一个由A*A个方格组成的方块,分成k个B*B的方块和1个小方格后连乘一个由k+1个物体组成的环形物,如图,A=3时有两种分法。用C种颜色为每个方格染色,旋转得到的两种方案记为相同方案,问一共有多少种不同方案。

解法:如果将B*B的小正多边形看做一个整体,则B*B的方案数即外围“长度为k的项链”的颜色数,因此此题要嵌套两个polya分别对B*B和K+1个物体的方案数分别计算。

1.由于A巨大,因此不能提供枚举A*A-1的质因子确定B,首先将A*A-1化为(A+1)(A-1)然后分别求得A+1和A-1的质因子,然后将两个数的质因子分解结果合并,由于gcd(A+1,A-1)=1或2,因此对于A是奇数 的情况合并时需要合并2的幂数。由此得到若干对<B,K>;由于B*B*k=(A+1)(A-1),因此在枚举B是可以顺便处理k的质因子分解:初始化时与B*B的分解结果相同,每次dfs时B的质因子p增加delta倍,k的质因子p减少2delta倍。

2.对于一个B*B的正方形,共有四种置换(A,B,C,D)  (C,A,D,B) (D,C,B,A) (B,D,A,C)

          对于第1中置换,方案数为C^(B*B)

          对于第三种置换,如果B为偶数,根据前上面的B/2行便可得到下面的B/2行;若B为奇数,第B/2+1行任意染色,因此方案数C^(B*B+1)/2

          对于第二四种置换,如果B为偶数,左上角四分之一区域染色方案唯一确定整个B*B的染色方案;如果B为奇数,左上角(B+1)/2*(B-1)/2唯一确定其他三个角,同时中间格子随意染色,因此方案数为C^(B*B+3)/4

3.若B*B的不同方案是为c,则问题变为k个珠子的项链用c种颜色染色有多少种方案,通过k的质因子dfs求得所有可能的循环节个数得到方案数;同时中间的珠子有C中颜色,因此结果应乘C。

import java.util.Scanner;
public class Rotation3441 {
	long mod = 1000000007;
	long a,c,res,ans,k,color;
	long prime1[]=new long[100010],prime2[]=new long[100010];
	int count1[]=new int[100010],count2[]=new int[100010];
	void divide(long n,long[] prime,int[] count) {
		int cnt=count[0];//质因子数目
		long mx = (long) (Math.sqrt(n) + 3);
		if (n < mx)
			mx = n;
		for (int i = 2; i < mx; i++) {
			if (n == 1)
				break;
			if (n % i == 0) {
				prime[++cnt] = i;
				count[cnt] = 0;
				while (n % i == 0) {
					n /= i;
					count[cnt]++;
				}
			}
		}
		if (n != 1) {
			prime[++cnt] = n;
			count[cnt] = 1;
		}
		count[0]=cnt;
	}
	void dfs2(int now, long value, long euler) {//k
		if (now == count2[0] + 1) {		
			ans += (euler*pow(color, k / value))%mod;
			ans %= mod;
			return;
		}
		dfs2(now + 1, value, euler);
		long temp = prime2[now];
		for (int i = 1; i <= count2[now]; i++) {
			dfs2(now + 1, value * temp, euler * (prime2[now] - 1)
					* (temp / prime2[now])%mod);
			temp *= prime2[now];
			temp%=mod;
		}
	}

	void dfs1(int now, long value) {//b
		if (now == count1[0] + 1) {		
			res=(res+polya2(value))%mod;
			return;
		}
		dfs1(now + 1, value);
		long temp =prime1[now];
		for (int i = 1; i <= count1[now]; i++) {
			count2[now]-=2*i;
			dfs1(now + 1, value*temp);
			count2[now]+=2*i;
			temp *= prime1[now];
		}
	}
	long pow(long x, long p) {
		long ans = 1;
		while (p > 0) {
			if ((p & 1) == 1)
				ans = (ans * x) % mod;
			p >>= 1;
			x = (x * x) % mod;
		}
		return ans % mod;
	}
	long inv(long k) {// k在摸mod意义下的乘法逆元
		return pow(k%mod, mod - 2) % mod;
	}
	long solve() {
		if (a == 1)
			return c % mod;
		res=0;
		count1[0]=0;
		divide(a-1,prime1,count1);
		int temp=count1[0];
		divide(a+1,prime1,count1);
		if((a&1)==1)//合并a+1和a-1的质因子
		{
			 count1[1]+=count1[temp+1];
			 count1[temp+1]=count1[count1[0]];
			 prime1[temp+1]=prime1[count1[0]];
			 count1[0]--;
		}//初始时k的质因子等于b*b的质因子
		for(int i=0;i<=count1[0];i++){
			prime2[i]=prime1[i];
			count2[i]=count1[i];
		}//b*b的质因子只能取b的质因子的偶数倍次幂
		for(int i=1;i<=count1[0];i++)
			count1[i]/=2;
		dfs1(1,1);
		return res;
	}
	long polya1(long b) {// b*b
		long ans = 0;
		ans = pow(c, b * b);
		ans = (ans + pow(c, (b * b+1)/ 2 )) % mod;
		ans = (ans + 2 * pow(c, (b * b + 3) / 4)) % mod;
		return (ans * inv(4)) % mod;
	}
	long polya2(long b) {// k
		color = polya1(b);
		ans = 0;
		k=(a+1)*(a-1)/(b*b);
		dfs2(1, 1, 1);
		ans=(ans*c)%mod;
		return (ans * inv(k)) % mod;
	}
	void run() {
		Scanner scan = new Scanner(System.in);
		int cas = scan.nextInt();
		for (int i = 1; i <= cas; i++) {
			a = scan.nextLong();
			c = scan.nextLong();
			System.out.println("Case" + " " + i + ": " + solve());
		}
	}
	public static void main(String[] args) {
		new Rotation3441().run();
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值