zoukankan      html  css  js  c++  java
  • 模拟赛1102d1

    炮(cannon)
    【题目描述】
    众所周知,双炮叠叠将是中国象棋中很厉害的一招必杀技。炮吃子时必须
    隔一个棋子跳吃,即俗称“炮打隔子”。 炮跟炮显然不能在一起打起来,于是rly
    一天借来了许多许多的炮在棋盘上摆了起来……他想知道,在N×M的矩形方格
    中摆若干炮(可以不摆)使其互不吃到的情况下方案数有几种。
    棋子都是相同的。
    【输入说明】
    一行,两个正整数N和M。
    【输出说明】
    一行,输出方案数mod 999983。
    【样例输入】
    1 3
    【样例输出】
    7
    【数据范围】
    对于40%的数据,N<=4,M<=4
    对于70%的数据,N<=100,M<=8
    对于100%的数据,N<=100,M<=100

    /*
      动态规划,状态的表示很巧妙
      f[i][j][k]表示放了前i行,有j列放了1个,有k列放了2个,那么就有m-i-j列没放的方案数。然后完成转移。
    */
    #include<cstdio>
    #include<iostream>
    #define mod 999983LL
    #define N 110
    using namespace std;
    long long f[N][N][N],n,m;
    int main()
    {
        //freopen("cannon.in","r",stdin);
        //freopen("cannon.out","w",stdout);
        cin>>n>>m;
        f[0][0][0]=1;
        for(long long i=1;i<=n;i++)
          for(long long j=0;j<=m;j++)
            for(long long k=0;j+k<=m;k++)
            {
                f[i][j][k]=f[i-1][j][k];f[i][j][k]%=mod;//不放
                if(j>=1)f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1);f[i][j][k]%=mod;//空处放1个
                if(k>=1)f[i][j][k]+=f[i-1][j+1][k-1]*(j+1);f[i][j][k]%=mod;//1个处放1个
                if(j>=2)f[i][j][k]+=f[i-1][j-2][k]*(m-j-k+2)*(m-j-k+1)/2;f[i][j][k]%=mod;//空处放2个
                if(k>=2)f[i][j][k]+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2;f[i][j][k]%=mod;//1处放2个
                if(k>=1)f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1);f[i][j][k]%=mod;//空处和1个处
            }
        long long ans=0;
        for(long long j=0;j<=m;j++)
          for(long long k=0;j+k<=m;k++)
            ans+=f[n][j][k],ans%=mod;
        cout<<ans;
        fclose(stdin);fclose(stdout);
        return 0;
    }

    车(rook)
    【题目描述】
    众所周知,是中国象棋中最厉害的一子之一,它能吃到同一行或同一列
    中的其他棋显然不能在一起打起来,于是rly一天又借来了许多许多
    在棋盘上摆了起来……他想知道,在N×M的矩形方格中摆最多个数的车使
    其互不吃到的情况下方案数有几种。但是,由于上次摆炮摆得实在太累,他为
    了偷懒,打算增加一个条件:对于任何一个车A,如果有其他一个车B在它的上
    方(车B行号小于车A),那么车A必须在车B的右边(车A列号大于车B)。
    棋子都是相同的。
    【输入说明】
    一行,两个正整数N和M。
    【输出说明】
    一行,输出方案数的末尾50位(不足则直接输出)。
    【样例输入】
    2 2
    【样例输出】
    1
    【数据范围】
    对于20%的数据, N<=10, M<=10。
    对于40%的数据, N<=40, M<=40。
    对于70%的数据, N<=10000, M<=10000。
    对于100%的数据, N<=1000000, M<=1000000。

    /*
      我们保证n<m,因为题目要求尽量多放,所以一定放n个,答案就是C(m,n),直接上高精度会超时,要先分解一下质因数,然后再做。
    */
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define N 1000010
    #define M 101
    using namespace std;
    int f[N],prime[N],p[N],c[N],n,m,num;
    int a1[M],b1[M],c1[N];
    struct node
    {
        node()
        {
            memset(q,0,sizeof(q));len=0;
        }
        int q[M],len;
    };node ans;
    void gprime()
    {
        for(int i=2;i<=m;i++)
          if(!f[i])
          {
              prime[++num]=i;p[i]=num;
              for(int j=2;i*j<=m;j++)
                f[i*j]=1;
          }
    }
    void work1(int x)
    {
        for(int i=1;i<=num;i++)
        {
            while(x%prime[i]==0)c[i]++,x/=prime[i];
            if(!f[x]){c[p[x]]++;break;}
            if(x==1)break;
        }
    }
    void work2(int x)
    {
        for(int i=1;i<=num;i++)
        {
            while(x%prime[i]==0)c[i]--,x/=prime[i];
            if(!f[x]){c[p[x]]--;break;}
            if(x==1)break;
        }
    }
    node cheng(node a,int b)
    {
        memset(a1,0,sizeof(a1));
        memset(b1,0,sizeof(b1));
        node c;
        int len=a.len;
        for(int i=1;i<=a.len;i++)a1[i]=a.q[len-i+1];
        for(int i=1;i<=len;i++)
        {
            b1[i]+=a1[i]*b;
            b1[i+1]+=b1[i]/10;
            b1[i]%=10;
        }
        while(b1[len+1]>=10)
        {
            b1[len+2]=b1[len+1]/10;
            b1[len+1]%=10;
            len++;
        }
        if(b1[len+1])len++;
        c.len=min(50,len);
        for(int i=1;i<=c.len;i++)c.q[i]=b1[c.len-i+1];
        return c;
    }
    int main()
    {
        //freopen("rook.in","r",stdin);
        //freopen("rook.out","w",stdout);
        scanf("%d%d",&n,&m);
        if(n>m)swap(n,m);
        gprime();
        for(int i=m;i>=m-n+1;i--)
          work1(i);
        for(int i=1;i<=n;i++)
          work2(i);
        ans.len=1;ans.q[1]=1;
        for(int i=1;i<=num;i++)
          for(int j=1;j<=c[i];j++)
            ans=cheng(ans,prime[i]);
        for(int i=max(1,ans.len-49);i<=ans.len;i++)
          printf("%d",ans.q[i]);
        return 0;
    }

    皇后(queen)
    【题目描述】
    众所不知, rly现在不会玩国际象棋。但是,作为一个OIer, rly当然做过八
    皇后问题。这里再啰嗦几句,皇后可以攻击到同行同列同对角线,在n*n的方格
    中摆n个皇后使其互不攻击到,求不同的解的数量,这就是经典的n皇后问题。
    现在问题推广到n皇后问题,这个问题对于你而言实在是小菜一叠。但因为上一
    次rly把棋盘弄破了,又拿不出新的,所以rly打算难一点点,问题就是破棋盘上
    的n皇后问题。他想知道……(你们懂的)。
    棋子都是相同的。
    【输入说明】
    一行,一个正整数N。
    接下来N行,每行N个数,要么为0,表示没坏,要么1,表示坏了。
    【输出说明】
    一行,输出不同的解的数量。
    【样例输入】
    4
    1 0 1 1
    1 1 1 0
    0 1 1 1
    1 1 0 1
    【样例输出】
    1
    【数据范围】
    对于40%的数据, N<=13。
    对于100%的数据, N<=16。
    其中有30%的数据,棋盘没有破(你可以认为rly又去买了一个新的)。

    /*位运算优化n皇后*/
    #include<cstdio>
    #include<iostream>
    #define N 20
    using namespace std;
    int hang[N],n,ans;
    void dfs(int t,int a,int b,int c)//a,b,c都是二进制数,表示哪一列或对角线是否放过皇后,1为放过 
    {
        if(t==n+1)
        {
            ans++;return;
        }
        int S=((1<<n)-1)&(~(hang[t]|a|b|c));
        /*
          S是一个二进制数,表示当前可以往哪一列放皇后 
          (hang[t]|a|b|c)表示哪些列从列和对角线的角度来说都放过
          加上~(取反),表示哪些列可以放皇后 
        */ 
        while(S)
        {
            int x=S&(-S);//x表示可以当前放皇后的位置 
            dfs(t+1,a+x,(b+x)<<1,(c+x)>>1);
            S-=x;
        }
    }
    int main()
    {
        //freopen("queen.in","r",stdin);
        //freopen("queen.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
          {
              int x;scanf("%d",&x);
              hang[i]+=x<<(j-1);
          }
        dfs(1,0,0,0);
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    [背包]JZOJ 3232 【佛山市选2013】排列
    内核空间、用户空间、虚拟地址
    进程与线程的概念
    Python中字符串颜色
    socket编程
    模块与包
    常用模块
    面向对象进阶
    面向对象编程
    函数式编程
  • 原文地址:https://www.cnblogs.com/harden/p/6037878.html
Copyright © 2011-2022 走看看