Light oj 1332 - Kings in Chessboard

题目:Light oj 1332 - Kings in Chessboard

题意:对一个n*10的棋盘,像扫雷一样的每列放两个kings,求方案数

思路:构造转移矩阵  = = 


技巧:对于2^32或者2^64的操作可以直接忽略,最后根据正负处理


#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
struct Matrix
{
    int m[40][40];
}E,D;
int n_num=0;
int num[110];
bool is_ok(int n)
{
    if(__builtin_popcount(n)!=2)
        return false;
    string s="";
    int tmp=n;
    while(tmp)
    {
        if(tmp%2==1)
            s="1"+s;
        else
            s="0"+s;
        tmp/=2;
    }
    while(s.size()<10)
        s="0"+s;
    for(int i=0;i<9;i++)
        if(s[i]=='1'&&s[i+1]=='1')
            return false;
    return true;
}
bool is_right(int x,int y)
{
    string s="",t="";
    int cnt=x,tmp=y;
    while(cnt)
    {
        if(cnt&1)
            s="1"+s;
        else
            s="0"+s;
        cnt/=2;
    }
    while(tmp)
    {
        if(tmp&1)
            t="1"+t;
        else
            t="0"+t;
        tmp/=2;
    }
    while(s.size()<10)
        s="0"+s;
    while(t.size()<10)
        t="0"+t;
    for(int i=0;i<=9;i++)
    {
        if(s[i]=='1')
        {
            if(i-1>=0)
            {
                if(t[i-1]=='1')
                    return false;
            }
            if(i+1<=10)
            {
                if(t[i+1]=='1')
                    return false;
            }
            if(t[i]=='1')
                return false;
        }
    }
    return true;
}
void Pre()
{
    for(int i=1;i<=(1<<9)+(1<<8);i++)
    {
        if(is_ok(i))
            num[++n_num]=i;
    }
    memset(D.m,0,sizeof(D.m));
    for(int i=1;i<=n_num;i++)
        for(int j=1;j<=n_num;j++)
        {
            if(is_right(num[i],num[j]))
                D.m[i][j]=1;
        }
}
void init()
{
    for(int i=1;i<=n_num;i++)
        for(int j=1;j<=n_num;j++)
            E.m[i][j]=(i==j);
}
Matrix Multi(Matrix A,Matrix B)
{
    Matrix ans;
    for(int i=1;i<=n_num;i++)
        for(int j=1;j<=n_num;j++)
        {
            ans.m[i][j]=0;
            for(int k=1;k<=n_num;k++)
                ans.m[i][j]+=A.m[i][k]*B.m[k][j];
        }
    return ans;
}
Matrix Pow(Matrix A,int k)
{
    Matrix ans=E;
    while(k)
    {
        if(k&1)
        {
            k--;
            ans=Multi(ans,A);
        }
        else
        {
            k/=2;
            A=Multi(A,A);
        }
    }
    return ans;
}
int main()
{
    Pre();
    init();
    /*
    for(int i=1;i<=n_num;i++)
    {
        for(int j=1;j<=n_num;j++)
            printf("%d ",D.m[i][j]);
        printf("\n");
    }*/
    int t;
    scanf("%d",&t);
    for(int cases=1;cases<=t;cases++)
    {
        int n;
        scanf("%d",&n);
        Matrix tmp=Pow(D,n-1);
        Matrix cnt;
        for(int i=1;i<=n_num;i++)
            cnt.m[i][1]=1;
        Matrix ans;
        for(int i=1;i<=n_num;i++)
            for(int j=1;j<=1;j++)
            {
                ans.m[i][j]=0;
                for(int k=1;k<=n_num;k++)
                    ans.m[i][j]+=tmp.m[i][k]*cnt.m[k][j];
            }
        int sy=0;
        for(int i=1;i<=n_num;i++)
            sy+=ans.m[i][1];
        long long pp=1;
        pp<<=32;
        if(sy<0)
            sy=pp+sy;
        printf("Case %d: %lld\n",cases,sy);
    }
    return 0;
}




尼玛   前天浑浑噩噩的 跟喵呜大神说  这个题是 10*n 的棋盘,一共只放两个kings,那我该怎么处理当前的状态确保前面是否已经存在这样的两个kings了呢,然后巴拉巴拉各种混乱(然后才发现题目看错了)

好吧  那下面贴一个与本题不大相关的代码,就是把题目的每行恰有两个kings,变成整个棋盘只有两个kings的方案数

膜拜喵呜大神 

// 膜拜喵呜~
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;

#define MOD 4294967296ll

typedef struct
{
    unsigned long long a[70][70];
}Matrix;

typedef struct
{
    int st,num;
}State;

State av[70];
int two[1025];
int up;
Matrix ch,ans;

Matrix Mul(Matrix x,Matrix y)
{
    int i,j,k;
    Matrix ret;
    for (i=0;i<up;i++)
    {
        for (j=0;j<up;j++)
        {
            ret.a[i][j]=0;
            for (k=0;k<up;k++)
            {
                ret.a[i][j]=(ret.a[i][j]+(x.a[i][k]*y.a[k][j])%MOD)%MOD;
            }
        }
    }
    return ret;
}

unsigned long long F_pow(int n)
{
    Matrix ret=ans;
    Matrix x=ch;
    int i;
    while(n)
    {
        if (n & 1) ret=Mul(x,ret);
        x=Mul(x,x);
        n>>=1;
    }/*
    for (i=0;i<up;i++)
    {
        for (int j=0;j<up;j++)
            cout<<x.a[i][j]<<" ";
        cout<<endl;
    }*/
    unsigned long long r=0;
    for (i=0;i<up;i++)
    {
        //cout<<ret.a[i][0]<<endl;
        if (av[i].num==2) r=(r+ret.a[i][0])%MOD;
    }
    return r;
}

bool Check(State x,State y)
{
    int i,s=0;
    if (x.num>y.num) return false;
    if (x.num+two[y.st]!=y.num) return false;
    if (((x.st<<1) & y.st)!=0) return false;
    if (((x.st>>1) & y.st)!=0) return false;
    if ((x.st & y.st)!=0) return false;
    //printf("%d %d->%d %d\n",x.st,x.num,y.st,y.num);
    return true;

}

void Pre()
{
    int i,j;
    up=0;
    memset(two,0,sizeof(two));
    for (i=0;i<(1<<10);i++)
    {
        for (j=0;j<10;j++)
        {
            if ((i & (1<<j))!=0) two[i]++;
        }
    }
    for (i=0;i<10;i++)
    {
        for (j=i+2;j<10;j++)
        {
            av[up].st=(1<<i)|(1<<j);
            av[up++].num=2;
        }
    }
    for (i=0;i<10;i++)
    {
        av[up].st=(1<<i);
        av[up++].num=1;
        av[up].st=(1<<i);
        av[up++].num=2;
    }
    av[up].st=0;
    av[up++].num=0;
    av[up].st=0;
    av[up++].num=1;
    av[up].st=0;
    av[up++].num=2;
    for (i=0;i<up;i++)
    {
        for (j=0;j<up;j++)
        {
            if (Check(av[i],av[j])) ch.a[j][i]=1;
            else ch.a[j][i]=0;
        }
    }
    memset(ans.a,0,sizeof(ans.a));
    for (i=0;i<up;i++)
    {
        if (two[av[i].st]==av[i].num) ans.a[i][0]=1;
    }
}

int main()
{
    int i,j,n,T;
    Matrix s;
    scanf("%d",&T);
    Pre();
    while(T--)
    {
        scanf("%d",&n);
        cout<<F_pow(n-1)<<endl;
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值