hdu5181 numbers

Posted turkeyghb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu5181 numbers相关的知识,希望对你有一定的参考价值。

链接

 

numbers

Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 196608/196608 K (Java/Others)
Total Submission(s): 146    Accepted Submission(s): 45


Problem Description
Now you have a stack and n numbers 1,2,3,,n. These n numbers are pushed in the order and popped if the number is at the top of the stack. You can read the sample to get more details.
This question is quite easy. Therefore I must give you some limits.
There are m limits, each is expressed as a pair<A,B> means the number A must be popped before B.
Could you tell me the number of ways that are legal in these limits?
I know the answer may be so large, so you can just tell me the answer mod 1000000007(109+7).
 

 

Input
The first line contains an integer T(about 5),indicating the number of cases.
Each test case begins with two integers n(1n300) and m(1m90000).
Next m lines contains two integers A and B(1An,1Bn)
(P.S. there may be the same limits or contradict limits.)
 

 

Output
For each case, output an integer means the answer mod 1000000007.
 

 

Sample Input
5 1 0 5 0 3 2 1 2 2 3 3 2 2 1 2 3 3 3 1 2 2 3 3 1
 

 

Sample Output
1 42 1 2 0
Hint
The only legal pop-sequence of case 3 is 1,2,3. The legal pop-sequences of case 4 are 2,3,1 and 2,1,3.
 

 

Source
 

 

 

 

首先考虑没有限制的情况,这其实就是一个卡特兰数,我们令f[i][j]表示将编号为i到j的元素进栈出栈一共有多少方案,那么枚举k,表示最后一个出栈元素是谁,那么就将问题划分为了两部分,f[i][k-1],f[k+1][j],可以用类似于区间dp的方法来结局。

如果有了限制,最暴力的办法自然是对于每一次枚举的i,j,k,判断一下是否和某一个条件矛盾,对于一组A,B,矛盾的时候就是i<=A,B<=j,并且A==k||(A>k&&B<k),我们不需要考虑A,B同时小于k大于等于i时会不会出现不合法的情况,因为这是一个子问题,在计算该子问题时已经排除掉了所有不合法的情况。

那么有代码:

技术分享图片
 1 #include<cstdio>
 2 #include<cstring>
 3 #define ll long long
 4 using namespace std;
 5 const int inf=305;
 6 const int mod=1e9+7;
 7 int T;
 8 int n,m,A[inf*inf],B[inf*inf];
 9 ll f[inf][inf];
10 int main()
11 {
12     scanf("%d",&T);
13     while(T--){
14         memset(f,0,sizeof(f));
15         scanf("%d%d",&n,&m);
16         for(int i=1;i<=m;i++)scanf("%d%d",&A[i],&B[i]);
17         for(int i=1;i<=n+1;i++)
18             for(int j=0;j<i;j++)
19                 f[i][j]=1;
20         for(int l=1;l<=n;l++){
21             for(int i=1;i+l-1<=n;i++){
22                 int j=i+l-1;
23                 for(int k=i;k<=j;k++){
24                     int flag=0;
25                     for(int o=1;o<=m;o++){
26                         if(A[o]==k&&B[o]>=i&&B[o]<=j){
27                             flag=1;
28                             break;
29                         }
30                         if(A[o]>k&&A[o]<=j&&B[o]<k&&B[o]>=i){
31                             flag=1;
32                             break;
33                         }
34                     }
35                     if(!flag)f[i][j]=(f[i][j]+f[i][k-1]*f[k+1][j])%mod;
36                 }
37             }
38         }
39         printf("%lld\n",f[1][n]);
40     }
41     return 0;
42 }
View Code

 

考虑如何优化dp的过程,如果我们可以在O1的时间内完成对i,j,k是否合法进行判断,那么我们可以得到一个Tn^3的复杂度,那么我们设一个数组c[i][j][k]表示i,j,k是否和某个限制发生了矛盾,那么我们可以将这个数组预处理出来,外层枚举每一个k,内层枚举每一个限制,当某个限制满足A==k||(A>k&&B<k)时,意味着当前的k对于一些i,j是不合法的,而当且仅当i<=A,B<=j时才会出现i,j,k不合法的情况,即i<=min(A,B),j>=max(A,B),如果我们暴力的去更新c[i][j][k]数组,复杂度依旧是Tmn^3的,所以我们可以将i,j视为一个二维平面,因为我们有当前能影响到的i,j的范围,所以相当于对某一个矩阵集体打上标记,而这个过程可以通过差分轻松的实现,然后再统计一下前缀和即可得到每个点被影响了几次,只要被影响了,那么c[i]j[][k]就是1,即不合法。注意求前缀和的数组是三维的,s[k][i][j],表示在枚举某一个k时,哪些i,j会是不合法的。

技术分享图片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define ll long long
 5 using namespace std;
 6 const int inf=305;
 7 const int mod=1e9+7;
 8 int T;
 9 int n,m,A[inf*inf],B[inf*inf];
10 ll f[inf][inf];
11 bool c[inf][inf][inf];
12 int s[inf][inf][inf]; 
13 int main()
14 {
15     scanf("%d",&T);
16     while(T--){
17         memset(f,0,sizeof(f));
18         memset(c,0,sizeof(c));
19         memset(s,0,sizeof(s));
20         scanf("%d%d",&n,&m);
21         for(int i=1;i<=m;i++)scanf("%d%d",&A[i],&B[i]);
22         for(int i=1;i<=n+1;i++)
23             for(int j=0;j<i;j++)
24                 f[i][j]=1;
25         for(int k=1;k<=n;k++){
26             for(int j=1;j<=m;j++){
27                 if(A[j]==k||(A[j]>k&&B[j]<k)){
28                     int u=max(A[j],B[j]),v=min(A[j],B[j]);
29                     s[k][1][u]++;s[k][v+1][u]--;
30                 }
31             }
32         }
33         for(int k=1;k<=n;k++){
34             for(int i=1;i<=n;i++){
35                 for(int j=1;j<=n;j++){
36                     s[k][i][j]=s[k][i-1][j]+s[k][i][j-1]-s[k][i-1][j-1]+s[k][i][j];
37                     if(s[k][i][j])c[i][j][k]=1;
38                 }
39             }
40         }
41         for(int l=1;l<=n;l++){
42             for(int i=1;i+l-1<=n;i++){
43                 int j=i+l-1;
44                 for(int k=i;k<=j;k++){
45                     if(!c[i][j][k])f[i][j]=(f[i][j]+f[i][k-1]*f[k+1][j])%mod;
46                 }
47             }
48         }
49         printf("%lld\n",f[1][n]);
50     }
51     return 0;
52 }
View Code

 

以上是关于hdu5181 numbers的主要内容,如果未能解决你的问题,请参考以下文章

hdu 5181 numbers

[Luogu5181][COCI2009]GENIJALAC

代码片段:Shell脚本实现重复执行和多进程

HDU2848 Number Cutting Game

HDU 1771 Number Sequence

HDU 5972 Regular Number