http://www.elijahqi.win/archives/576
题目描述
最近IK正在做关于地形建模的工作。其中一个工作阶段就是把一些山排列成一行。每座山都有各不相同的标号和高度。为了遵从一些设计上的要求,每座山都设置了一个关键数字,要求对于每座山,比它高且排列在它前面的其它山的数目必须少于它的关键数字。 显然满足要求的排列会有很多个。
对于每一个可能的排列,IK生成一个对应的标号序列和等高线序列。标号序列就是按顺序写下每座山的标号。
等高线序列就是按顺序写下它们的高度。例如有两座山,这两座山的一个合法排列的第一座山的标号和高度为1和3,而第二座山的标号和高度分别为2和4,那么这个排列的标号序列就是1 2,而等高线序列就是3 4.
现在问题就是,给出所有山的信息,IK希望知道一共有多少种不同的符合条件的标号序列和等高线序列。
输入输出格式
输入格式:
输入第一行给出山的个数N。接下来N行每行有两个整数,按照标号从1到N的顺序分别给出一座山的高度和关键数。
输出格式:
输出两个用空格分隔开的数,第一个数是不同的标号序列的个数,第二个数是不同的等高线序列的个数。这两个答案都应该对2011取模,即输出两个答案除以2011取余数的结果
输入输出样例
输入样例#1:
2
1 2
2 2
输出样例#1:
2 2
说明
对于所有的数据,有1<=N<=1000,所有的数字都是不大于10^9的正整数。
功夫不负有心人,经历22次wa之后我终于ac了
给定一些山,每座山有一个高度和一个关键值,现在要将这些山排成一个序列,要求每座山之前高度高于它的山的数量不能超过它的关键值
问题一:求合法的标号序列数
问题二求的是高度序列数(有重复的算一种)
针对问题一我们考虑,因为高度较小的山对于高度较大的山是没有影响的
不妨我们把高度从大到小排列,然后再依次往序列里插 同时 我们高度相同时还要针对 关键字从小到大排列,这题的难点在于有相同的我们不妨把相同高度的数据按关键值x从小到大排序,这样再插入a[i]时,和他一样高的点一定插在他的前面,但并不比它大否则我们做不了dp的
假设我们新插入一个数,那么这个数一定比我们前面的所有数都要小或者一样大
设data[i]的关键值为x,则a[i]可以插在前1~x个空档,根据乘法原理,ans=ans*min(i,data[i].x)。此外还有之前已经解决过的相同情况如何处理,因此是ans=ans*min(i,data[i].x+tmp),tmp表示在i之前,有多少个高度为a[i]的点。
第二问 :更难。
不过我们一点一点分析来做
为了保证没有重复的,所以我们选择如果有相同高度就把他们拿来一起做
在这个子问题里我们再使用动态规划
比如重复的球有n个 我们前面已经插入了m-1个球 拥有m个空挡可以插
那么相当于一个小学奥数计数问题,只不过更复杂,限制更多
关于动态规划的转移方程 我们设f[i][j]表示前i个空里插前j个球的方案数
f[i][j]=∑[i−1]k
f[i][j]=f[i-1][j]+f[i-1][j-1]+f[i-1][j-2]+……+f[i-1][1] (1<=j<=data[i].x)
这个里面分别就代表第i个插在第J个j-1个j-2个中的各种方案数 加起来就是第i个插在前j个中的方案数
化简o1转移
f[i][j]=f[i-1][j]+f[i][j-1]
最后再根据乘法原理就是我们的答案了(同第一问)
last数组用来求我们当前高度这个最长延伸到哪
#include<cstdio>
#include<algorithm>
#include<cstring>
#define mod 2011
#define N 1100
#define LL long long
struct node{
int key,h;
}data[N];
inline bool cmp (node a,node b){
if (a.h==b.h) return a.key<b.key;else return a.h>b.h;
}
inline int min(int x,int y){
return x<y?x:y;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int f[2][N],n,last[N];
int main(){
freopen("3255.in","r",stdin);
n=read();
for (int i=1;i<=n;++i) data[i].h=read(),data[i].key=read();
//for (int i=1;i<=n;++i) printf("%d %d\n",data[i].h,data[i].key);
std::sort(data+1,data+1+n,cmp);
int tmp=0;int ans=1;
for (int i=1;i<=n;++i){
if(data[i].h==data[i-1].h) tmp++;else tmp=0;
ans=(LL)ans*min(data[i].key+tmp,i)%mod;
}
printf("%d ",ans);tmp=1;
//for(int i=1;i<=n;++i) printf("%d %d\n",data[i].h,data[i].key);
for (int i=1;i<=n;i=i+tmp,tmp=1) {
for (int j=i+1;j<=n;++j) if (data[j].h==data[j-1].h) tmp++;else break;
last[i]=tmp;
}
// for (int i=1;i<=n;++i) printf("%d ",last[i]);printf("\n");
ans=1;int p=0;
for (int i=1;i<=n;i=i+last[i]){
p=0;
for (int j=1;j<=data[i].key;++j) f[0][j]=1;//前0个球放到任意个盒子里,都只有一种方案
// if (last[i]==1) continue;
for (int j=1;j<=last[i];++j){
for (int z=1;z<=i;++z){
f[p^1][z]=f[p^1][z-1];
//如果这个盒子i不能放,因为key[i]是递增的,所以i-1...1也不能放进这个盒子
if (z<=data[i+j-1].key)f[p^1][z]=(f[p^1][z-1]+f[p][z])%mod ;
}p^=1;
}
ans=(LL)ans*f[p][i]%mod;
}
printf("%d",ans);
return 0;
}