模拟赛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;
}
原文地址:https://www.cnblogs.com/harden/p/6037878.html