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$)

View Code

考试暴力($TLE 30$)

View Code

正解($AC$)

View Code

其实这个题思路很简单,因为总共有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. 划艇

题目描述

在首尔城中,汉江横贯东西。在汉江的北岸,从西向东星星点点地分布着 $N$ 个划艇学校,编号依次为 $1$ 到 $N$。每个学校都拥有若干艘划艇。同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同。颜色相同的划艇被认为是一样的。每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加。如果编号为 $i$ 的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在 $a_i~b_i$i​​ 至 bib_ibi​​ 之间任意选择(aibi)。

值得注意的是,编号为 $i$ 的学校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校派出的划艇数量。

输入所有学校的 $a_i,b_i$i​​,bi​​ 的值,求出参加庆典的划艇有多少种可能的情况,必须有至少一艘划艇参加庆典。两种情况不同当且仅当有参加庆典的某种颜色的划艇数量不同。

输入格式

第一行包括一个整数$ N$,表示学校的数量。
接下来 $N$ 行,每行包括两个正整数,用来描述一所学校。其中第 $i$ 行包括的两个正整数分别表示 $ai,bi(1≤a_i≤b_i≤10^9)$i​​,bi​​(1ai​​bi​​109​​)。$

输出格式

输出一行,一个整数,表示所有可能的派出划艇的方案数除以 $10^9+7$9​​+7 得到的余数。

样例

样例输入

2
1 2
2 3

样例输出

7

样例解释

在只有一所学校派出划艇的情况下有 $4$ 种方案,两所学校都派出划艇的情况下有$ 3 $种方案,所以答案为$ 7$。

数据范围与提示

子任务 1(9 分):$1≤N≤500$ 且对于所有的 $1≤i≤N$,保证 ai=bi。i​​=bi​​。

子任务 2(22 分):1≤N≤500∑(bi−ai)≤10^6。i=1N​​(bi​​ai​​)106​​。

子任务 3(27 分):1≤N≤100

子任务 4(42 分):1≤N≤500

 
 

C. 放棋子

题目描述

在一个n行m列的棋盘里放一些彩色的棋子,使得每个格子最多放一个棋子,且不同颜色的棋子不能在同一行或者同一列。有多少种方法?

例如:有两个白棋子和一个灰棋子,下面左边两种方法都是合法的,但右边两种都是非法的。
img  img109+9

输入格式

输入第一行为两个整数n, m, c,即行数、列数和棋子的颜色数。
第二行包含c个正整数,即每个颜色的棋子数。
所有颜色的棋子总数保证不超过nm。
N,M<=30 C<=10 总棋子数有大于250的情况

输出格式

输出仅一行,即方案总数除以 1,000,000,009的余数。

样例

样例输入

4 2 2
3 1

样例输出

8

数据范围与提示

30% n,m<=10

 

首先,定义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;
}
View Code

其实跟网上其他博客写的差不多。

posted @ 2019-07-09 21:25  Milk_Feng  阅读(43)  评论(0编辑  收藏  举报