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 p∗q
得到:
然后我们惊奇地发现,其中所有格子都出现了不重复的数,范围是
1
−
p
∗
q
1-p*q
1−p∗q
为什么呢?
证明:
可以一列一列来看。
那么我们发现,对于第一列都是模q=1的,对于第二列都是模q=0的,对于第三列都是模q=5的……依次类推。
所以我们只需要证明一个东西:当x取0到q-1时满足
x
∗
p
(
m
o
d
q
)
x*p(mod\ q)
x∗p(mod q)是互不相同的。
那么假设
x
1
∗
p
(
m
o
d
q
)
x1*p(mod\ q)
x1∗p(mod q)和
x
2
∗
p
(
m
o
d
q
)
x2*p(mod\ q)
x2∗p(mod q)相等
可列得:
x
1
∗
p
−
x
2
∗
p
=
y
∗
q
x1*p-x2*p=y*q
x1∗p−x2∗p=y∗q
然后解一解可以发现,
x
1
−
x
2
=
z
∗
q
(
z
∈
Z
)
x1-x2=z*q(z\in Z)
x1−x2=z∗q(z∈Z)
那么就证明了在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);
}