Evanyou Blog 彩带

洛谷 模拟城市2.0

一次洛谷月赛的T1,当时因为是信心赛,认为第一题应该不会太难,结果想了很久,直接额放弃正解选择暴力。。。简直就是巨坑的五维DP。。。mmd

题目背景

博弈正在机房颓一个叫做《模拟城市2.0》的游戏。

2048年,经过不懈努力,博弈终于被组织委以重任,成为D市市委书记!

他勤学好问,励精图治,很快把D市建设成富强民主文明和谐的美好城市。为了进一步深化发展,他决定在海边建立一个经济开发区。

题目描述

已知开发区的建筑地块是一个n×n的矩形,而开发区可以建造三种建筑: 商业楼,住宅楼,教学楼。这任何两座建筑可以堆叠,可以紧密相邻。他需要建造正好a座商业楼,b座住宅楼,c座教学楼。但是,城市建成后要应付检查,如果安排的太混乱会被批评。不过幸运的是,只有一条公路经过了该开发区的一侧,就是说,检察人员全程只能看到开发区的一面。

因此,他需要使得开发区建成后,从正面看去,只有一种类型的建筑。

一共有多少种满足条件的方案呢? 请输出方案数,并对10^9+7取模。

注意,对于同一个n,会有多组数据。

输入输出格式

输入格式:

第一行两个整数n,T

接下来T行,每行三个整数,表示该组数据的a,b,c

输出格式:

输出共T行,每行一个整数:表示各数据答案取模10^9+7的结果。

输入输出样例

输入样例#1: 
2 1
1 1 0
输出样例#1: 
4
输入样例#2: 
2 1
2 1 0
输出样例#2: 
8

说明

对于20%的数据,n2  a,b,c3  T5

对于另外10%的数据,n3  a,b,c4  T5

对于另外20%的数据,b=0

对于另外10%的数据,T10

对于全部100%的数据,a,b,c,n25  T5×105

样例1

 

样例2

 

Solution:

正解DP+组合数。(可能有多种方法,这里说其中的一种)我们发现,从正面看,横排放置的方块会互相影响(因为后面的可能比前面高,状态太复杂且不好转移),我们可以考虑纵向放置的方案数。纵列和纵列之间不会相互遮挡,因此方案数很好统计。

所以我们需要处理出纵列合法的方案数。虽然有三种方块,但我们只是需要一种漏在外面,所以可以把另外两种先不考虑,把它们合成一个状态。
处理任意一列合法方案数,可以dfs处理,也可以dpf[i][j][k][x][y]表示放置到第 i个格子,高度为 j,已经放置的最高高度为 k,已经用了 x个能看见的方块,y 个不能看见的方块的方案数边界条件:最高可以放到max(a,b,c),我们发现如果高度超过了所有种类颜色的数量,那么我们不可能使得这种情况合法。需要注意的是, y的最大值应当是2*max(a,b,c),因为我们把另外两种方块整合在一起了。那么就可以按顺序暴力的放置了。每个格子可能有两种操作,在它上面放一个新的格子,或者把下一个格子放到下一行去。如果下个位置的高度超过了枚举的最高高度,那么前面就没有格子可以遮挡它,它必须放能看见的种类,并更新最高高度。否则说明下个位置的高度不够高,会被前面已经放置的格子遮挡,那么放什么种类就都可以啦。

状态转移方程:

放到下一行:
f[i+1][0][k][x][y]+=f[i][k][k][x][y];
放到上面:
if (j==k)
f[i][j+1][k+1][x+1][y]+=f[i][j][k][x][y];
else
f[i][j+1][k][x+1][y]+=f[i][j][k][x][y],
f[i][j+1][k][x][y+1]+=f[i][j][k][x][y];
复杂度O(n5)

为了方便统计,标程的 i计算到了n+1这样我们就可以处理出,因为i=n的所有情况都统计在了f[n+1][0]中。

这样我们就可以处理出g[i][j]表示在某一列上,用了i 种能看见的方块,j 种遮挡住的方块的方案数。每一行的方案数处理出来了,我们就可以开始真正的dp了。

dp[i][j][k]表示从左往右(正面)放到第i 列,用了j 种能看见的方块, k种遮挡的方块的方案数。dp[i+1][j+x][y+k]+=dp[i][j][k]*g[x][y];

复杂度O(n5)

处理出dp数组之后,实际上我们就得到了答案的一部分。如果只有两种方块可供放置,那么dp数组就是答案了就是我先前说的b=0的部分。

但是我们有三种方块。考虑让一种方块漏出来,那么另外两种方块的位置是可以随意组合的(因为并不会漏在外面)。所以还需要处理一下组合数。数据范围很小,可以直接预处理。如果只让一种(如住宅楼)能看见,那么方案数已经显而易见了。

dp[n][a][b+c]*C[c+b][b];

dp表示使得a 漏在外面,b,c遮住的方案数。C 数组是组合数,表示一共有b+c个位置,b、c随意组合的方案数。

那么最终答案就呼之欲出了。

ans=(dp[n][a][b+c]*C[b+c][b])+(dp[n][b][c+a]*C[c+a][c])+(dp[n][c][a+b]*C[a+b][a]); 

我们发现其实对于同一个n ,处理出来的dp数组是完全一致的,我们可以将a,b,c处理到范围的最大值25,就可以O(1)查询得到对于不同a,b,c的答案了。
时间复杂度O(2*n5+常数),完全是可以过的(除非评测鸡太差)。 

输入数据很大,为了尽量减少常数的影响,建议使用快速读入。不过经过测试, scanf 理论是可以通过的。此题没有刻卡常,各位可以随意踩std。

代码:

 1 #include<bits/stdc++.h>
 2 #define mod 1000000007
 3 #define N 27
 4 using namespace std;
 5 inline int read()
 6 {
 7     int x=0,f=1;char ch=getchar();
 8     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 9     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
10     return x*f;
11 }
12 int f[N][N][N][N][N<<1],g[N][N<<1],C[N<<1][N<<1],dp[N][N][N<<1];
13 int ans,a,b,c,n,m,T;
14 inline void add(int &x,int y){x+=y;if (x>=mod) x-=mod;}
15 main()
16 {
17     n=read(),T=read();
18     m=25;
19     f[0][0][0][0][0]=1;
20     for (int i=0;i<n;i++)
21         for (int j=0;j<=m;j++)
22             for (int k=j;k<=m;k++)
23                 for (int x=k;x<=m;x++)
24                     for (int y=0;y<=(m<<1);y++)
25                         if (f[i][j][k][x][y])
26                         {
27                             add(f[i+1][0][k][x][y],f[i][j][k][x][y]);
28                             if (j==k)
29                                 add(f[i][j+1][k+1][x+1][y],f[i][j][k][x][y]);
30                             else
31                                 add(f[i][j+1][k][x+1][y],f[i][j][k][x][y]),
32                                 add(f[i][j+1][k][x][y+1],f[i][j][k][x][y]);
33                         }
34     for (int k=0;k<=m;k++)
35         for (int x=k;x<=m;x++)
36             for (int y=0;y<=(m<<1);y++)
37                 add(g[x][y],f[n][0][k][x][y]);
38     C[0][0]=1;
39     for(int i=1;i<=(m<<1);i++)
40     {
41         C[i][0]=1;
42         for(int j=1;j<=i;j++)
43         {
44             C[i][j]=C[i-1][j-1]+C[i-1][j];
45             if (C[i][j]>=mod) C[i][j]-=mod;
46         }
47     }
48     dp[0][0][0]=1;
49     for (int i=0;i<n;i++)
50         for (int j=0;j<=m;j++)
51             for (int k=0;k<=(m<<1);k++)
52                 if (dp[i][j][k])
53                     for(int x=0;j+x<=m;x++)
54                         for(int y=0;y+k<=(m<<1);y++)
55                             add(dp[i+1][j+x][y+k],(1ll*dp[i][j][k]*g[x][y])%mod);
56     while(T--)
57     {
58         a=read(),b=read(),c=read();
59         ans=0;
60         add(ans,(1ll*dp[n][a][b+c]*C[b+c][b])%mod);
61         add(ans,(1ll*dp[n][b][c+a]*C[c+a][c])%mod);
62         add(ans,(1ll*dp[n][c][a+b]*C[a+b][a])%mod);
63         printf("%d\n",ans);    
64     }
65 }

 

posted @ 2017-11-18 16:16  five20  阅读(269)  评论(0编辑  收藏  举报
Live2D