2019-7-7考试总结
A. 排序
题目描述
小A有一个$1-2^N$的排列$A[1..2^N]$,他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的$i(1<=i<=N)$,第$i$种操作为将序列从左到右划分为$2^{N-i+1}$段,每段恰好包括$2^{i-1}$个数,然后整体交换其中两段.
小A想知道可以将数组$A$从小到大排序的不同的操作序列有多少个,小$A$认为两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同).
下面是一个操作事例: $N=3,A[1..8]=[3,6,1,2,7,8,5,4]$.
第一次操作,执行第$3$种操作,交换$A[1..4]$和$A[5..8]$,交换后的$A[1..8]$为$[7,8,5,4,3,6,1,2]$.
第二次操作,执行第$1$种操作,交换$A[3]$和$A[5]$,交换后的$A[1..8]$为$[7,8,3,4,5,6,1,2]$.
第三次操作,执行第$2$中操作,交换$A[1..2]$和$A[7..8]$,交换后的$A[1..8]$为$[1,2,3,4,5,6,7,8]$.
输入格式
第一行,一个整数$N$
第二行,$2^N$个整数,$A[1..2^N]$
输出格式
一行,一个整数,表示可以将数组$A$从小到大排序的不同的操作序列的个数。
样例
样例输入
3
7 8 5 6 1 2 4 3
样例输出
6
数据范围与提示
对于$30\%$的数据,$1<=N<=4$; 对于全部的数据,$1<=N<=12$。
题解
这个题考试的时候一开始想到了要用一颗树来做,然而并没有考虑全。
打了一个$dp$,结果最后发现树上的$dp$是错的,因为每层交换使之成立的情况有好几种。
考试错解($WA 15$)
考试暴力($TLE 30$)
正解($AC$)
其实这个题思路很简单,因为总共有2的整数次幂倍,所以很容易想到完全二叉树,
如果要使最后的序列合法,那么肯定每一层都要合法。
看样例: $7 8 5 6 1 2 4 3$
我们发现假如两个数一组,那么会分成${7 8}$,${5 6}$,${1 2}$,${4 3}$。
那么肯定要让$4$、$3$互换,${7 8}$,${5 6}$,${1 2}$,${3 4}$。
再让4个数一组,${{7 8} {5 6}},{{1 2} {3 4}}$,肯定要让${7 8}$、${5 6}$互换。
变成$ {{5 6} {7 8}}$,${{1 2} {3 4}}$。
最后肯定在让最后两个区间互换。
如果我们找到一种合法的方案,那么换的顺序可以随意,也就是方案数为它的阶乘。
然后考虑每一层,
可以分4种情况:
1、不合法的数的组数有$>2$个,那么不可能通过交换$1$次使其合法。
2、不合法的数的组数有$2$个,那么我们让他们两两交换,也就是 左儿子交换右儿子、左儿子交换左儿子......看它合不合法。
这可能会有好几种合法的情况,比如:$1 4 3 2$
既可以变成$ 1 2 3 4$,也可以变成$3 4 1 2$。
3、不合法的数的组数有$1$个,那么考虑,
一个例子:$ 1 4 2 3$,那么这种情况不可能一次交换合法。
所以要判断一下这两个数的差。
4、没有不合法的数,那么就不需交换。
这里的不合法有两种情况,一种是左儿子大于右儿子,另一种是两儿子的差大于$1<<(n-deep-1)$。
我们要上传区间最大值。
具体实现看代码。
B. 划艇
题目描述
输入格式
输出格式
样例
数据范围与提示
C. 放棋子
题目描述
输入格式
输出格式
样例
数据范围与提示
首先,定义F[i][j][k]表示前k个不同颜色的棋子占据了i行j列。
那么F[i][j][k]=∑∑F[l][r][k-1]*C(n-l,i-l)*C(m-r,j-r)*num[k]个同色棋子占据i-l行j-r列的方案数。
这个式子的意思也就是放了k-1种颜色的棋子,然后再放第k种。
那么F[l][r][k-1]也就是前k-1种颜色的棋子占据了l行r列。
我们还剩i-l行,j-r列,要把它填满。
对于后面的那个乘的那个数用g[i][j][k]表示。
g[i][j][k]表示k个同色棋子占据了i行j列的方案数。
那么g[i][j][k]=C(i*j,k)-∑∑g[l][r][k]*C(i,l)*C(j,r)。
也就是总的方案数减去不合法的方案数。
最后再转移。
AC代码(AC)
#include<iostream> #include<cstring> #include<cstdio> #define Reg register #define mod 1000000009 using namespace std; int n,m,maxx,c,num[950]; long long ans,C[950][950],f[50][50][950],g[50][50][950]; int main() { scanf("%d%d%d",&n,&m,&c); for(Reg int i=1;i<=c;++i) { scanf("%d",&num[i]); maxx=max(maxx,num[i]); } for(Reg int i=0,p;i<=n*m;++i) { for(Reg int j=1;j<=n*m;++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; C[i][0]=1; C[i][i]=1; } f[0][0][0]=1; for(Reg int k=1;k<=maxx;++k) { for(Reg int i=1;i<=n;++i) { for(Reg int j=1;j<=m;++j) { if(i*j<k) continue; g[i][j][k]=C[i*j][k]%mod; long long p=0; for(Reg int x=0;x<=i;++x) for(Reg int y=0;y<=j;++y) if(x!=i||y!=j) p=(p+(g[x][y][k]*C[i][x]%mod*C[j][y])%mod+mod)%mod; g[i][j][k]=(g[i][j][k]-p)%mod; } } } for(Reg int k=1;k<=c;++k) { for(Reg int i=0;i<=n;++i) { for(Reg int j=0;j<=m;++j) { f[i][j][k]=0; for(Reg int x=0;x<=i-1;++x) for(Reg int y=0;y<=j-1;++y) f[i][j][k]=(f[x][y][k-1]*C[n-x][i-x]%mod*C[m-y][j-y]%mod*g[i-x][j-y][num[k]]%mod+f[i][j][k])%mod; } } } for(Reg int i=1;i<=n;++i) for(Reg int j=1;j<=m;++j) ans=(ans+f[i][j][c])%mod; printf("%lld",ans%mod); return 0; }
其实跟网上其他博客写的差不多。