JLOI2013(BZOJ3193)“地形生成”

来源:互联网 发布:淘宝虚拟宝贝的类目 编辑:程序博客网 时间:2024/06/05 10:42

不得不说这道题对于我来说还是挺难的,dp+组合数学,题解中短短不到50行代码让蒟蒻领悟到人生真谛。。。

题目:HYSBZ - 3193

最近IK正在做关于地形建模的工作。其中一个工作阶段就是把一些山排列成一行。每座山都有各不相同的标号和高度。为了遵从一些设计上的要求,每座山都设置了一个关键数字,要求对于每座山,比它高且排列在它前面的其它山的数目必须少于它的关键数字。

 显然满足要求的排列会有很多个。对于每一个可能的排列,IK生成一个对应的标号序列和等高线序列。标号序列就是按顺序写下每座山的标号。等高线序列就是按顺序写下它们的高度。例如有两座山,这两座山的一个合法排列的第一座山的标号和高度为1和3,而第二座山的标号和高度分别为2和4,那么这个排列的标号序列就是1 2,而等高线序列就是3 4.
 现在问题就是,给出所有山的信息,IK希望知道一共有多少种不同的符合条件的标号序列和等高线序列。

分析:

1.对于第一个问题,首先我们可以知道,比一座山矮的山是不会影响这座山的,所以说我们将山由高到低排一个序(使得我们的转移是不会与之前冲突的),对于排好序的第i座山,由于它之前只能有ki座山比它(ki为第i座山的关键值),或者说它的前面只有k座山比它高,那么它就只有min(i,ki)种位置方案可供选择,使用乘法原理即可。
再来可能会有山的高度相等的情况。假设在第i座山之前有一个座山j和它高度相等但是kj>ki的,我们用乘法原理会计算到冲突状态的,所以在高度为第一关键字的前提下,我们还必须以k值由小到大的排,而对于每一座山它之前会有一些和它等高的山,数目为x,那么这座山现在有min(i+ki)+x这么多这种方案。
2.第二个问题其实是将第一题范围缩小了的,即ans1>=ans2的,而多出来的那些方案全是由于山的高度相同造成的。那么在此时,使得两种方案不同的是跟在某一座山后面山的数量不同。则对于一段区间(l,r)中山的高度相同时,设dp[i][j]为把(l,r)中的前i个山放到第j个位置的方案数,然后就开始状态转移啦。
dp[i][j]=dp[i-1][0]+dp[i-1][1]+……+dp[i-1][j-1]+dp[i-1][j],又因为dp[i][j-1]=dp[i-1][0]+dp[i-1][1]+……+dp[i-1][j-1],所以我们可得:dp[i][j]=dp[i-1][j]+dp[i][j-1],记得dp[0][j]=0,dp[i][0]=1的初始化。
在状态转移完后将dp[r][0~maxj]加起来就是这一个等高区间的方案数,最后仍然是用乘法原理将每一个区间的方案数相乘即可。
其实这里还可以优化一下空间复杂度,将i的那一维略去,每次记得清空一下数组就行。状态转移为:
dp[j]=dp[j-1]+dp[j](原因是由于枚举顺序是由小到大的,dp[j-1]已经算过了,此时就是dp[i][j-1],而dp[j]才刚刚算到里面存的是dp[i-1][j]的值)。
时间复杂度为O(n^3)具体能过的原因应该是:1.实际上大部分数据是跑不满n^3的;2.官方数据水了(真正原因。。。)
看到这里人,还请原谅博主的啰嗦微笑。。。

代码:

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int mod=2011;int n,ans1=1,ans2=1;struct node{int h,k;bool operator <(const node &s1)const{if(h!=s1.h) return h>s1.h;return k<s1.k; }}a[10001];int dp[10001];int main(){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d %d",&a[i].h,&a[i].k);sort(a+1,a+n+1);for(int l=1;l<=n;l++){int r=l;while(r<=n&&a[r].h==a[l].h) r++;r--;memset(dp,0,sizeof(dp));//每一次dp都只是针对目前的(l,r)来说的dp[0]=1;for(int i=l;i<=r;i++){ans1=ans1*(min(l,a[i].k)+i-l)%mod;for(int k=1;k<=min(l-1,a[i].k-1);k++)dp[k]=(dp[k]+dp[k-1])%mod;//其实是就是dp[i][k]=dp[i-1][k]+dp[i][k-1] }int sum=0;for(int i=0;i<=min(l-1,a[r].k);i++)sum=(sum+dp[i])%mod;ans2=(ans2*sum)%mod;l=r;}printf("%d %d",ans1,ans2); return 0;}


原创粉丝点击