bzoj3193 luogu3255 jloi2013地形生成

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值