【题目描述】:
在一个圆上均匀分布p*q个点{A1,A2,A3…Ap*q},Ai与Aj的距离为min{abs(i-j),p*q-abs(i-j)},在上面选任意个点(可以选0个),如果选择的点中存在两个点距离为p或q,就会发生排斥反应,求不发生排斥反应的方案总数。
【题目解法】:
不愧是集训队的神题
考虑对于给圆周上的点编号
然后一个点x,他向右走表示x+q,向下走表示x+p
那么就构成了一个q*p的矩阵
然后我们从矩阵中选出一些点使他们两两不相(最后一行和第一行,最后一列和第一列也算相邻)
然后状压dp+矩阵乘法就好了
然后发现每一行不相邻的状态大概100+个
那么复杂度大概就是(100^3 log M)
注意特判m=1的情况
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=205;
const long long maxmod=19921107;
int n,m,tot,c[N],ans;
long long a[N][N],b[N][N],tmp[N][N];
inline long long getint()
{
long long x=0; char c=getchar(); bool flag=false;
while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
if (c=='-') flag=true,c=getchar();
while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
if (flag) return -x; else return x;
}
void init()
{
n=getint(); m=getint();
}
void dfs(int x,int state)
{
if (x>n) {if ((state>>(n-1)&1)&&(state&1)&&(n!=1)) return; c[++tot]=state; return;}
dfs(x+1,state*2); if (state%2==0) dfs(x+1,state*2+1);
}
void build()
{
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
if ((c[i]&c[j])==0) a[i][j]++;
for (int i=1; i<=tot; i++) b[i][i]=1;
}
inline void mulaa()
{
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
{
tmp[i][j]=0;
for (int k=1; k<=tot; k++) tmp[i][j]+=a[i][k]*a[k][j];
tmp[i][j]%=maxmod;
}
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
a[i][j]=tmp[i][j];
}
inline void mulab()
{
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
{
tmp[i][j]=0;
for (int k=1; k<=tot; k++) tmp[i][j]+=a[i][k]*b[k][j];
tmp[i][j]%=maxmod;
}
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
b[i][j]=tmp[i][j];
}
void solve()
{
if (m==1) {printf("%d\n",tot); return;}
for (m--;m;m/=2,mulaa()) if (m&1) mulab();
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
if ((c[i]&c[j])==0) ans=(ans+b[i][j])%maxmod;
printf("%d\n",ans);
}
int main()
{
init();
dfs(1,0);
build();
solve();
return 0;
}