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状丫70
/*记忆化状丫 70*/#include#include #include
正解也是dp
/*考试的时候写的状丫 70 正解也是dp 不过这个状态还是不好想的fijk 前i行 j列空 k列放一个 那么放两个的就确定了 不用放进状态具体的哪几行 放了一个....不重要 不影响转移的样子然后每次在这一行里放 分别考虑放几个 放在什么情况的列里然后O(1)转移 总复杂度 O(n*m*m)有这么几种情况放0个 1放1个 2 放在没有炮的列 或者 3 有一个炮的列放2个 4 都放在没有炮的同一列 或者 5 放在没有炮的两列 或者 6 有一个炮的两列 或者 7 一个在没有炮的 一个在有一个炮的 显然 上面的 4 一个格格放了俩 23333 脑抽了一会 */#include#define maxn 110#define ll long long#define mod 999983#ifdef unix#define LL "%lld\n"#else#define LL "%I64d\n"#endifusing namespace std;ll n,m,f[maxn][maxn][maxn],ans;ll C(ll x){ return x*(x-1)/2;}int main(){ freopen("cannon.in","r",stdin); freopen("cannon.out","w",stdout); scanf("%d%d",&n,&m); f[0][0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=m;k++){ if(j+k>m)continue;f[i][j][k]=(f[i][j][k]+f[i-1][j][k])%mod;//1 if(j)f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k]*(m-j-k+1)%mod)%mod;//2 if(k&&j 1)f[i][j][k]=(f[i][j][k]+f[i-1][j-2][k]*C(m-j-k+2)%mod)%mod;//5 if(j 1)f[i][j][k]=(f[i][j][k]+f[i-1][j+2][k-2]*C(j+2)%mod)%mod;//6 if(j&&k)f[i][j][k]=(f[i][j][k]+f[i-1][j][k-1]*j*(m-j-k+1)%mod)%mod;//7 } for(int i=0;i<=m;i++) for(int j=0;j<=m;j++) if(i+j<=m)ans=(ans+f[n][i][j])%mod; printf(LL,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。暴力40
/*没看出组合数2333 记忆化40*/#include#include #define maxn 1010#define ll long longusing namespace std;ll n,m,f[maxn][maxn],ans;ll Dfs(ll now,ll pre){ if(f[now][pre])return f[now][pre]; if(now==m+1)return 1; int r=0; for(int i=pre;i<=n;i++) r+=Dfs(now+1,i); return f[now][pre]=r;}int main(){ freopen("rook.in","r",stdin); freopen("rook.out","w",stdout); cin>>n>>m; if(n
正解C
/*先分解一下质因数 然后把除法去掉 对于只有乘法 只保留最后100位 快*/#include#include #include #define maxn 1010#define maxm 1000010using namespace std;int n,m,a[maxn],num,c[maxm/10],prime[maxm/10];bool f[maxm];void Prime(){ for(int i=2;i<=n;i++){ if(f[i]==0)prime[++num]=i; for(int j=1;j<=num;j++){ if(i*prime[j]>n)break; f[i*prime[j]]=1; if(i%prime[j]==0)break; } }}void Mul(int a[maxn],int x){ for(int i=1;i<=a[0];i++) a[i]=a[i]*x; for(int i=1;i<=a[0];i++) if(a[i]>9){ a[i+1]+=a[i]/10;a[i]%=10; } while(a[a[0]+1]){ a[0]++;a[a[0]+1]=a[a[0]]/10;a[a[0]]%=10; } if(a[0]>100)a[0]=100;}void Get1(int x){ for(int i=1;i<=num;i++){ int r=x,P=prime[i];//这里会爆掉..以后改成除了 while(r){ c[i]+=r/P;r/=P; } }}void Get2(int x){ for(int i=1;i<=num;i++){ int r=x,P=prime[i]; while(r){ c[i]-=r/P;r/=P; } }}int main(){ freopen("rook.in","r",stdin); freopen("rook.out","w",stdout); scanf("%d%d",&n,&m); if(n =1;i--) printf("%d",a[i]); fclose(stdin);fclose(stdout); return 0;}
皇后(queen)
【题目描述】众所不知, rly现在不会玩国际象棋。但是,作为一个OIer, rly当然做过八皇后问题。这里再啰嗦几句,皇后可以攻击到同行同列同对角线,在n*n的方格中摆n个皇后使其互不攻击到,求不同的解的数量,这就是经典的n皇后问题。现在问题推广到n皇后问题,这个问题对于你而言实在是小菜一叠。但因为上一次rly把棋盘弄破了,又拿不出新的,所以rly打算难一点点,问题就是破棋盘上的n皇后问题。他想知道……(你们懂的)。棋子都是相同的。【输入说明】一行,一个正整数N。接下来N行,每行N个数,要么为0,表示没坏,要么1,表示坏了。【输出说明】一行,输出不同的解的数量。【样例输入】41 0 1 11 1 1 00 1 1 11 1 0 1【样例输出】1【数据范围】对于40%的数据, N<=13。对于100%的数据, N<=16。其中有30%的数据,棋盘没有破(你可以认为rly又去买了一个新的)。暴力70
/*裸暴力70*/#include#include #define ll long long#define maxn 20using namespace std;int n,g[maxn][maxn],falg,cnt,f[maxn],v[maxn*10],c[maxn][maxn],r[maxn][maxn];ll ans;void Dfs(int x){ if(x==n+1){ ans++;return; } for(int i=1;i<=n;i++) if(f[i]==0&&v[c[x][i]]==0&&v[r[x][i]]==0){ v[c[x][i]]=1;v[r[x][i]]=1; f[i]=1;Dfs(x+1);f[i]=0; v[c[x][i]]=0;v[r[x][i]]=0; }}void dfs(int x){ if(x==n+1){ ans++;return; } for(int i=1;i<=n;i++) if(f[i]==0&&v[c[x][i]]==0&&v[r[x][i]]==0&&g[x][i]==0){ v[c[x][i]]=1;v[r[x][i]]=1; f[i]=1;dfs(x+1);f[i]=0; v[c[x][i]]=0;v[r[x][i]]=0; }}void Solve1(){ Dfs(1);}void Solve2(){ dfs(1);}int main(){ freopen("queen.in","r",stdin); freopen("queen.out","w",stdout); cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ cin>>g[i][j]; if(g[i][j]==1)falg=1; } for(int i=1;i<=n;i++){ int x=1,y=i;cnt++; while(x<=n&&y<=n){ c[x][y]=cnt;x++;y++; } } for(int i=2;i<=n;i++){ int x=i,y=1;cnt++; while(x<=n&&y<=n){ c[x][y]=cnt;x++;y++; } } for(int i=1;i<=n;i++){ int x=1,y=i;cnt++; while(x<=n&&y>0){ r[x][y]=cnt;x++;y--; } } for(int i=2;i<=n;i++){ int x=i,y=n;cnt++; while(x<=n&&y>0){ r[x][y]=cnt;x++;y--; } } if(!falg)Solve1(); else Solve2(); cout< <
位运算优化
/*位运算由优化的n皇后问题get了 大方向的复杂度是没变的只不过 剪枝 + 位运算 常熟小的多了具体的 以行为阶段 记录之前每一列的信息以及两条对角线的信息列好办 关键是这个对角线比较丑 因为他是斜着的假设当前行我们放在了i这里 那么下一行的话 就是i左边 和i右边不能放 这里利用位运算的左移右移就好了然后还有些小技巧 就是枚举当前行放在哪利用lowbit 找最小的不是0的位在哪还有就是 状态记录的时候0表示还可以放但是涉及到lowbit这个 我们用的时候去一下反1 表示还可以放 这就非常优美了 */#include#define maxn 20#define ll long long#ifdef unix#define LL "%lld\n"#else#define LL "%I64d\n"#endifusing namespace std;int n,c[maxn];ll ans;void Dfs(int now,int H,int D1,int D2){ if(now==n+1){ ans++;return; } int S=((1< 0){ int x=S&(-S); Dfs(now+1,H+x,(D1+x)<<1,(D2+x)>>1); S-=x; }}int main(){ freopen("queen.in","r",stdin); freopen("queen.out","w",stdout); scanf("%d",&n);int x; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ scanf("%d",&x); c[i]+=(x<