11.2 上午考试
noip模拟题day1
——棋盘上的问题
day1模拟题 By FancyCoder
总览(Overview)
注意事项:
共3道题目,时间2.5小时。
Pascal选手允许使用math库和ansistring。
C++选手开放使用STL。
允许使用64位整型(int64或long long)。
题目名称 炮 车 皇后
程序名 cannon rook queen
输入文件名 cannon.in rook.in queen.in
输出文件名 cannon.out rook.out queen.out
测试点数目 10 10 10
测试点分值 10 10 10
时间限制 1s 1s 1s
空间限制 128M 128M 128M
题目类型 传统题 传统题 传统题
炮(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
/* dp[i][j][k] 表示前i行 放1个的有j列 放2个的的有k列的方案数 则放了0个的列为q=m-j-k 对于第i行 六种情况 1.不放 2.在放了0个的列中选1列放 3.在放了1个的列中选1列放 4.在放了0个的列中选2列放 5.在放了1个的列中选2列放 6.在放了0个的列和放了1个的列各选1列放 */ #include<iostream> #include<cstdio> #include<cstring> #define LL long long #define mod 999983 #define maxn 110 using namespace std; LL n,m,ans; LL dp[maxn][maxn][maxn]; LL init() { LL x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } LL C(LL n,LL m) { LL ret=1; for(LL i=1;i<=m;i++) ret=ret*(n-i+1)/i; ret%=mod; return ret; } int main() { freopen("cannon.in","r",stdin); freopen("cannon.out","w",stdout); n=init();m=init(); dp[0][0][0]=1; for(LL i=1;i<=n;i++) { for(LL j=0;j<=m;j++) { for(LL k=0;k<=m;k++) { if(j+k>m)continue; dp[i][j][k]+=dp[i-1][j][k],dp[i][j][k]%=mod; if(j-1>=0)dp[i][j][k]+=dp[i-1][j-1][k]*C(m-j-k+1,1)%mod,dp[i][j][k]%=mod; if(j-2>=0)dp[i][j][k]+=dp[i-1][j-2][k]*C(m-j-k+2,2)%mod,dp[i][j][k]%=mod; if(j+1<=m&&k-1>=0)dp[i][j][k]+=dp[i-1][j+1][k-1]*C(j+1,1)%mod,dp[i][j][k]%=mod; if(j+2<=m&&k-2>=0)dp[i][j][k]+=dp[i-1][j+2][k-2]*C(j+2,2)%mod,dp[i][j][k]%=mod; if(k-1>=0)dp[i][j][k]+=(dp[i-1][j][k-1]*C(j,1)%mod)*C(m-j-k+1,1)%mod,dp[i][j][k]%=mod; } } } for(LL i=0;i<=m;i++) for(LL j=0;j<=m;j++) ans+=dp[n][i][j],ans%=mod; cout<<ans<<endl; 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。
/* 观察后可以发现答案为C(n,m) 原来分解完之后 直接成就不超时 我还打了高精快速幂 恶心的高精度...... */ #include<iostream> #include<cstdio> #include<cstring> #define LL long long #define maxn 1000010 using namespace std; LL n,m,tot; LL prime[maxn],f[maxn]; LL ans[210],t[210],r[210],a[maxn]; LL init() { LL x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void prepare() { for(LL i=2;i<=m;i++) { if(!f[i])prime[++tot]=i; for(LL j=1;j<=tot&&prime[j]*i<=m;j++) { f[prime[j]*i]=1; if(i%prime[j]==0)break; } } } void Mul(LL a[110],LL b[110]) { memset(r,0,sizeof(r)); LL l1=a[0],l2=b[0]; for(LL i=1;i<=l1;i++) { LL x=0; for(LL j=1;j<=l2;j++) { r[i+j-1]+=a[i]*b[j]+x; x=r[i+j-1]/10; r[i+j-1]%=10; } r[i+l2]+=x; } LL l3=l1+l2; while(l3>l1&&r[l3]==0)l3--; while(l3>50)l3--;r[0]=l3; memcpy(a,r,sizeof(r)); } void mul(LL t[110],LL x) { LL r=0; for(LL i=1;i<=t[0];i++) { t[i]=t[i]*x+r; r=t[i]/10; t[i]%=10; } while(r) { t[++t[0]]=r%10; r/=10; if(t[0]==100)break; } } void pow(LL x,LL w) { if(w==0) { memset(t,0,sizeof(t)); t[0]=1;t[1]=1; return; } pow(x,w/2);Mul(t,t); if(w&1)mul(t,x); } LL min(LL x,LL y) { return x<=y?x:y; } int main() { freopen("rook.in","r",stdin); freopen("rook.out","w",stdout); n=init();m=init(); if(n>m)swap(n,m); prepare(); for(LL i=1;i<=tot;i++) { LL ha=m; while(ha) { a[i]+=ha/prime[i]; ha/=prime[i]; } } for(LL i=1;i<=tot;i++) { LL ha=n; while(ha) { a[i]-=ha/prime[i]; ha/=prime[i]; } } for(LL i=1;i<=tot;i++) { LL ha=m-n; while(ha) { a[i]-=ha/prime[i]; ha/=prime[i]; } } ans[0]=1;ans[1]=1; for(LL i=1;i<=tot;i++) { pow(prime[i],a[i]); Mul(ans,t); } for(LL i=min(50,ans[0]);i>=1;i--) cout<<ans[i]; fclose(stdin);fclose(stdout); 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<iostream> #include<cstdio> #include<cstring> using namespace std; int n,ans; int a[50][50]; int lie[50],zx[50],yx[50]; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void dfs(int t) { if(t==n+1) { ans++; return; } for(int j=1;j<=n;j++) { if(a[t][j])continue; if(lie[j])continue; if(zx[t+j])continue; if(yx[j-t+n])continue; lie[j]=1; zx[t+j]=1; yx[j-t+n]=1; dfs(t+1); lie[j]=0; zx[t+j]=0; yx[j-t+n]=0; } } int main() { freopen("queen.in","r",stdin); freopen("queen.out","w",stdout); n=init(); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) a[i][j]=init(); } dfs(1); printf("%d\n",ans); fclose(stdin);fclose(stdout); return 0; }
/* 位运算优化的n皇后问题 */ #include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,ans;int hang[20]; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void dfs(int t,int L,int X,int Y)//L存的列的状态 X,Y存的对角线的状态 在其二进制中1代表已经放过0表示没放过 { if(t==n+1) { ans++; return; } int S=((1<<n)-1)&(~(hang[t]|L|X|Y)); /* S存的能放的位置 先把行 列 对角线或起来现在1表示已经放过 然后取反后1代表没放过能放得到能放的 */ while(S) { int x=S&(-S);//lowbit找能放的位置-->即找1 dfs(t+1,L+x,(X+x)<<1,(Y+x)>>1); //L,X,Y能更新放的位置(对角线到下一行会移一列) S-=x; } } int main() { freopen("queen.in","r",stdin); freopen("queen.out","w",stdout); n=init(); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { int x;x=init(); if(x)hang[i]+=1<<(j-1); } } dfs(1,0,0,0); printf("%d\n",ans); fclose(stdin);fclose(stdout); return 0; }