UOJ Logo zhouzixuan的博客

博客

bzoj 2126: 排斥反应

2016-03-18 16:29:55 By zhouzixuan
【题目描述】:
    在一个圆上均匀分布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;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。