jzoj1915. 【2011集训队出题】排斥反应

Description

在一个圆上均匀分布pq个点{A1,A2,A3…Apq},Ai与Aj的距离为min{abs(i-j),p*q-abs(i-j)},在上面选任意个点(可以选0个),如果选择的点中存在两个点距离为p或q,就会发生排斥反应,求不发生排斥反应的方案总数。

Input

输入的第一行包含两个整数,分别表示p和q

Output

输出一个整数,表示方案总数,由于这个题答案可能很大,只要输出答案mod 19921107

Sample Input

1 6

Sample Output

18

Data Constraint

对于5%的数据,p=1,q<=10^6
  对于15%的数据,p=1,q<=10^9
  对于100%的数据,p<=10,q<=10^9,p和q互质

题解

这题好秒啊♂。
虽说连15分都八会,而且一点也不吸引人。

首先对于p=1的情况(%15)
那么我们可以考虑设一个dp方程:
f [ i ] [ 0..1 ] f_{[i][0..1]} f[i][0..1]表示当前第i个格子填0/1的答案。
这个其实很好转移,但是这样只能拿5分的好成绩。
但是,我们观察柿子,可以发现答案其实就是斐波那契数列的第q项加上第q-2项。
矩阵优化即可得到15分。

那么当p>1时呢?
由于同时保证p和q有点烦,直接维护显然不太现实。
得要换个思路来做。

这里有个神奇的方法:
假设现在询问p=5,q=6
那么我们考虑列出一个方格表:
(1,1)这个位置是1,其余都是0。现在从(1,1)出发,每次往右走一格就是+q,往下走一格就是+p。当然,如果超过了是 m o d   p ∗ q mod\ p*q mod pq
得到:
在这里插入图片描述
然后我们惊奇地发现,其中所有格子都出现了不重复的数,范围是 1 − p ∗ q 1-p*q 1pq
为什么呢?

证明:
可以一列一列来看。
那么我们发现,对于第一列都是模q=1的,对于第二列都是模q=0的,对于第三列都是模q=5的……依次类推。
所以我们只需要证明一个东西:当x取0到q-1时满足 x ∗ p ( m o d   q ) x*p(mod\ q) xp(mod q)是互不相同的。
那么假设 x 1 ∗ p ( m o d   q ) x1*p(mod\ q) x1p(mod q) x 2 ∗ p ( m o d   q ) x2*p(mod\ q) x2p(mod q)相等
可列得: x 1 ∗ p − x 2 ∗ p = y ∗ q x1*p-x2*p=y*q x1px2p=yq
然后解一解可以发现, x 1 − x 2 = z ∗ q ( z ∈ Z ) x1-x2=z*q(z\in Z) x1x2=zq(zZ)
那么就证明了在p*q以内是不会发生重复的。

那么再反过来看这道题,我们发现,问题就变成了:
在p*q的格子中放入若干个棋子,棋子之间不能相连(首尾上下相接也算不合法)
p那么小,状压。状压状态又有很多没有用的状态就优化掉,最多只有123种。
那么愉快矩乘优化即可。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int mo=19921107;

struct node
{
	long long a[3][3];
};
struct node1
{
	long long a[124][124];
};

int p,q,gs,op[1024],mi[12];
node f;
node1 zy;

node1 chengg(node1 x,node1 y,int p)
{
	node1 c;
	memset(c.a,0,sizeof(c.a));
	for (int i=1;i<=p;i++)
	{
		for (int j=1;j<=p;j++)
		{
			for (int k=1;k<=p;k++)
			{
				c.a[i][j]=(c.a[i][j]+x.a[i][k]*y.a[k][j])%mo;
			}
		}
	}
	return c;
}

node1 qsm1(node1 a,long long b)
{
	node1 t;
	memcpy(t.a,a.a,sizeof(t.a));
	node1 y;
	memcpy(y.a,a.a,sizeof(y.a));
	while (b>0)
	{
		if ((b&1)==1) t=chengg(t,y,gs);
		y=chengg(y,y,gs);
		b/=2;
	} 
	return t;
}

node cheng(node x,node y)
{
	node c;
	memset(c.a,0,sizeof(c.a));
	for (int i=1;i<=2;i++)
	{
		for (int j=1;j<=2;j++)
		{
			for (int k=1;k<=2;k++)
			{
				c.a[i][j]=(c.a[i][j]+x.a[i][k]*y.a[k][j])%mo;
			}
		}
	}
	return c;
}

node qsm(node a,long long b)
{
	node t;
	t.a[1][1]=0;t.a[1][2]=1;t.a[2][1]=1;t.a[2][2]=1;
	node y;
	memcpy(y.a,a.a,sizeof(y.a));
	while (b>0)
	{
		if ((b&1)==1) t=cheng(t,y);
		y=cheng(y,y);
		b/=2;
	} 
	return t;
}

long long get(int x)
{
	node t;
	t.a[1][1]=0;t.a[1][2]=1;t.a[2][1]=1;t.a[2][2]=1;
	node y=qsm(t,x-2);
	memset(t.a,0,sizeof(t.a));
	t.a[1][2]=1;
	y=cheng(y,t);
	return y.a[2][2];
}

int main()
{
	freopen("data.in","r",stdin);
	mi[0]=1;
	for (int i=1;i<=10;i++)
	{
		mi[i]=mi[i-1]*2;
	}
	scanf("%d%d",&p,&q);
	if (p==1)
	{
		long long a=get(q);
		long long b=get(q+2);
		printf("%lld\n",(a+b)%mo);
		return 0;
	}
	for (int i=0;i<=mi[p]-1;i++)
	{
		if (i==128)
		{
			int j=0;
		}
		bool pd=true;
		for (int j=2;j<=p;j++)
		{
			if ((mi[j-1]&i)!=0 && (mi[j-2]&i)!=0)
			{
				pd=false;
				break;
			}
		}
		if ((mi[0]&i)!=0 && (mi[p-1]&i)!=0)
		{
			pd=false;
		}
		if (pd==true)
		{
			gs++;
			op[gs]=i;
		} 
	}
	if (q==1)
	{
		printf("%d\n",gs);
		return 0;
	}
	for (int i=1;i<=gs;i++)
	{
		for (int j=1;j<=gs;j++)
		{
			if ((op[i]&op[j])==0)
			{
				zy.a[i][j]=1;
			}
		}
	}
	zy=qsm1(zy,q-2);
	long long ans=0;
	for (int i=1;i<=gs;i++)
	{
		for (int j=1;j<=gs;j++)
		{
			if ((op[i]&op[j])==0)
			{
				ans=(ans+zy.a[i][j])%mo;
			}
		}
	}
	printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值