POLYA合集 II

hdu 2481 Toy

解法借鉴http://hi.baidu.com/aekdycoin/item/517784ec0bf4450b560f1dd1 这篇博文,核心为关系式的推导。
具体实现过程中两个要点:
1、无法用传统方法求逆元,需借助二分模拟大数乘法取模运算
2、对于旋转1个单位这类置换,在该条件下不变的方案为5种。
#include<cstdio>
#include<algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef __int64 ll;
ll mod;
const int MAXN = 32000;
bool isPrime[MAXN] = {false};
int prime[MAXN], np = 0;
int nn;
ll bigMult(ll a, ll b)
{
	ll res = 0;
	if (a < 0) a += mod;
	if (b < 0) b += mod;
	while (b)
	{
		if (b & 1)
		{
			res += a;
			if (res >= mod) res -= mod;
		}
		a <<= 1;
		if (a >= mod) a -= mod;
		b >>= 1;
	}
	return res;
}
const ll bf[2][2] = {3,1,-1,0};
const ll bb[2][2] = {3,1,0,0};
struct matrix
{
	ll da[2][2];
	matrix(){}
	void init()
	{
		memset(da, 0, sizeof da);
		da[0][0] = da[1][1] = 1;
	}
	matrix operator * (const matrix & a)
	{
		matrix res;
		for (int i = 0; i< 2; ++i)
		for (int j = 0; j< 2; ++j)
		{
			res.da[i][j] = 0;
			for (int k = 0; k< 2; ++k)
				res.da[i][j] = (res.da[i][j] + bigMult(da[i][k], a.da[k][j]))%mod;
		}
		return res;	
	}
	matrix operator ^ (int a)
	{
		matrix res, p = *this;
		res.init();
		while  ( a)
		{
			if (a & 1) res = res * p;
			p = p*p;
			a >>= 1;
		}
		return res;
	}
};
void initPrime()
{
	for (int i = 2; i< MAXN; ++i)
	{
		if(!isPrime[i]) prime[np++] = i;
		for (int j = 0; j<np && i*prime[j]<MAXN; ++j)
		{
			isPrime[i*prime[j]] = true;
			if (i % prime[j] == 0) break;
		}
	}
}
int fenzi[100][2];
int nfen;
void divide(int a)
{
	for (int i = 0; i< np && prime[i]*prime[i] <= a; ++i)
	{
		if (a % prime[i] == 0)
		{
			fenzi[nfen][0] = prime[i]; fenzi[nfen][1] = 0;
			while (a % prime[i] == 0)
			{
				a /= prime[i];
				fenzi[nfen][1]++;
			}
			nfen++;
		}
	}
	if (a != 1)
	{
		fenzi[nfen][0] = a; fenzi[nfen++][1] = 1;
	}
}
ll resAll;
ll getCir(int t)
{
	ll res = 0;
	if (t == 1) return 1;
	if (t == 2) return 5;
	matrix ff, bg;
	memcpy(bg.da, bb, 32); memcpy(ff.da, bf, 32);
	bg = bg*(ff^(t-2));
	res = (bg.da[0][0] + (bg.da[0][0] - bg.da[0][1] - 1)*2%mod)%mod;
	if (res < 0) res += mod;
	return res;
}
void dfs(int i, int p1, int p2, int x)
{
	if (i == nfen)
	{
		int t = nn/x;
		resAll = (resAll + bigMult(getCir(t), x/p1*p2))%mod;		
	}else
	{
		int cnt = 0, p = fenzi[i][1], q = fenzi[i][0];
		for ( ;cnt <= p; ++cnt)
		{
			if (!cnt) dfs(i+1, p1, p2, x);
			else dfs(i+1, p1*q, p2*(q-1), x);
			x *= q;
		}
	}
}
ll solve()
{
	nfen = 0;
	resAll = 0;
	divide(nn);
	dfs(0, 1, 1, 1);
	return resAll/nn;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif
    initPrime();
    while (scanf("%d%I64d", &nn, &mod) != EOF)
    {
    	mod *= nn;
    	printf("%I64d\n", solve()%(mod/nn));
    }
    return 0;
}


hdu 4187 Alphabet Soup

表示智商上受碾压,看了别人的题解才知道要用KMP,不过对polya的理解也加深了一层
polya的核心在于找出满足条件的置换。若各个节点的度数为0、90、180、270,则等同于经典polya问题;但若度数为20、70、110、160、200、250、290、340,则可以两两合并,化简为4个点的环用m^2种颜色着色的问题。引入KMP正是为了找出合并的关系
#include<cstdio>
#include<algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef __int64 ll;
const ll mod = 100000007;
const int MAXS = 360005;
const int MAXN = 605;
bool isPrime[MAXN] = {false};
int prime[MAXN], np = 0, nn;
ll col;
ll mpow(ll a, ll b)
{
	ll res = 1, p = a%mod;
	while (b)
	{
		if (b & 1) res = res*p%mod;
		p = p*p%mod;
		b >>= 1;
	}
	return res;
}
void initPrime()
{
	for (int i = 2; i< MAXN; ++i)
	{
		if(!isPrime[i]) prime[np++] = i;
		for (int j = 0; j<np && i*prime[j]<MAXN; ++j)
		{
			isPrime[i*prime[j]] = true;
			if (i % prime[j] == 0) break;
		}
	}
}
int fenzi[100][2];
int nfen;
void divide(int a)
{
	for (int i = 0; i< np && prime[i]*prime[i] <= a; ++i)
	{
		if (a % prime[i] == 0)
		{
			fenzi[nfen][0] = prime[i]; fenzi[nfen][1] = 0;
			while (a % prime[i] == 0)
			{
				a /= prime[i];
				fenzi[nfen][1]++;
			}
			nfen++;
		}
	}
	if (a != 1)
	{
		fenzi[nfen][0] = a; fenzi[nfen++][1] = 1;
	}
}
ll resAll;
void dfs(int i, int p1, int p2, int x)
{
	if (i == nfen)
	{
		int t = nn/x;
		resAll = (resAll + mpow(col, t) * (x/p1*p2) % mod)%mod;		
	}else
	{
		int cnt = 0, p = fenzi[i][1], q = fenzi[i][0];
		for ( ;cnt <= p; ++cnt)
		{
			if (!cnt) dfs(i+1, p1, p2, x);
			else dfs(i+1, p1*q, p2*(q-1), x);
			x *= q;
		}
	}
}
ll solve()
{
	nfen = 0;
	resAll = 0;
	divide(nn);
	dfs(0, 1, 1, 1);
	return resAll*mpow(nn, mod-2)%mod;
}
int ss[MAXS], pp[MAXS], next[MAXS];
int getNext()
{
	next[0] = -1;
	for (int i = 1, j = next[0]; i< nn; ++i)
	{
		while (j > -1 && pp[j+1]!=pp[i]) j = next[j];
		if (pp[j+1] == pp[i]) ++j;
		next[i] = j;
	}
	int tp = nn-1-next[nn-1];
	if (nn % tp == 0) return tp;
	return nn;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif
	initPrime();
	while (scanf("%I64d%d", &col, &nn) && (col != -1))
	{
		for (int i = 0; i< nn; ++i)
		{
			scanf("%d", ss+i);
		}
		sort(ss, ss+nn);
		for (int i = 1; i< nn; ++i)
		{
			pp[i-1] = ss[i] - ss[i-1];
		}
		pp[nn-1] = 360000 - ss[nn-1] + ss[0];
		int len = getNext();
		col = mpow(col, len);
		nn /= len;
		printf("%I64d\n", solve());
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值