BZOJ 3193: [JLOI2013]地形生成【计数dp

80 篇文章 0 订阅
6 篇文章 0 订阅

Description

 
 
最近IK正在做关于地形建模的工作。其中一个工作阶段就是把一些山排列成一行。每座山都有各不相同的标号和高度。为了遵从一些设计上的要求,每座山都设置了一个关键数字,要求对于每座山,比它高且排列在它前面的其它山的数目必须少于它的关键数字。
 显然满足要求的排列会有很多个。对于每一个可能的排列,IK生成一个对应的标号序列和等高线序列。标号序列就是按顺序写下每座山的标号。等高线序列就是按顺序写下它们的高度。例如有两座山,这两座山的一个合法排列的第一座山的标号和高度为1和3,而第二座山的标号和高度分别为2和4,那么这个排列的标号序列就是1 2,而等高线序列就是3 4.
 现在问题就是,给出所有山的信息,IK希望知道一共有多少种不同的符合条件的标号序列和等高线序列。
 

Input

输入第一行给出山的个数N。接下来N行每行有两个整数,按照标号从1到N的顺序分别给出一座山的高度和关键数。
 

Output

 
输出两个用空格分隔开的数,第一个数是不同的标号序列的个数,第二个数是不同的等高线序列的个数。这两个答案都应该对2011取模,即输出两个答案除以2011取余数的结果
 

Sample Input


2
1 2
2 2

Sample Output


2 2

HINT

对于所有的数据,有1<=N<=1000,所有的数字都是不大于109的正整数。

Source


//===================================================================================

嗯看了Q巨的题解【跪地


计数dp……不会啊QAQ

好嘛……第一问的答案就是直接从高到矮直接xjb插进去就好……每个山可以放的地方数量乘起来,反正高的对矮的才有影响,而且影响的效果一样

第二问……好难啊QwQ

首先在排序的时候,高度为第一关键字降序排列,key值为第二关键字升序排列

因为高度相同的相互交换对答案没有贡献……所以强行让高度相同的里面key值小的放前面,于是对于每一组高度相同的单独dp一下

考虑f[i][j]表示考虑到当前高度中的第i座山,放在j位置,且前i-1座山都在j位置前面的放法

显然f[i][j]=∑f[i-1][k](0<=k<j)

然后发现f[i]只受到f[i-1]的影响,于是只用一维数组记录就好

#include<bits/stdc++.h>
#define MAXN 1005
#define MOD 2011
using namespace std;	int n;
struct t1{
	int key,height;
	bool operator < (const t1 &x) const{
		return height^x.height? height>x.height : key<x.key;
	}
}a[MAXN];

int f[MAXN];
int ans1=1,ans2=1;

int main(){
	freopen("1.in","r",stdin);
	freopen("orz.out","w",stdout);
	scanf("%d",&n);	
	for(int i=1;i<=n;++i)	scanf("%d%d",&a[i].height,&a[i].key);
	sort(a+1,a+n+1);
	
	for(int i=1,j=1;i<=n;i=j){
		memset(f,0,sizeof f);
		f[0]=1;
		int tmp=0;

		for(j=i;j<=n&&a[i].height==a[j].height;++j){
			for(int k=1;k<i && k<a[j].key;++k)
				f[k]+=f[k-1],f[k]%=MOD;
			ans1*=(min(a[j].key,i)+j-i),ans1%=MOD;//,printf("%d\n",ans1);
		}
//		printf("key [ %d ] = %d\n",j-1,a[j-1].key);
		for(int k=0;k<i&&k<a[j-1].key;++k)
			tmp+=f[k],tmp%=MOD;

//		for(int k=0;k<=n;++k)	printf("%d\n",f[k]);

//		puts("");

		ans2*=tmp,ans2%=MOD;
			
	}
	printf("%d %d\n",ans1,ans2);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值