[JLOI2013]地形生成[组合计数]

题意

(n) 元素各有一个高度 (h) 和关键数字 (b) 。求有多少个下标序列和高度序列,满足对任意 (i)(j< i)(h_j < h_i)(j) 个数小于 (b_i)

  • (n leq 1000).

分析

  • 因为只有比 (i) 高的元素才会影响 (i) ,所以考虑把所有元素按照高度降序排列。

  • 下标序列

    • 考虑每个元素插入之前的排列中,一共有 (min(b_i,i)) 种方案。

    • 考虑相同高度的元素按照 (b) 的大小升序排列,这样能够保证后放的元素一定可以放在先放的元素后面。

  • 高度序列

    • 做法类似之前,只是处理相同元素时要求每次要保证 每次放的位置 (geq) 上一次放的位置
  • 总时间复杂度为 (O(n^2))

处理组合计数问题的技巧:插入数字或者保留位置;
排列 ( ightarrow) 组合:强制放置有序即可;

代码

#include<bits/stdc++.h>
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].last,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
inline int gi(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch))	{if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
	return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=1007,mod=2011;
void add(int &a,int b){a+=b;if(a>=mod) a-=mod;}
int n;
int ans1=1,ans2=1,f[N][N],s[N][N];
struct data{
	int a,b;
	data(){}data(int a,int b):a(a),b(b){}
	bool operator <(const data &rhs)const{
		if(a!=rhs.a) return a>rhs.a;
		return b<rhs.b;
	}
}v[N];
int main(){
	n=gi();
	rep(i,1,n) v[i].a=gi(),v[i].b=gi()-1;
	sort(v+1,v+1+n);
	for(int i=1,j=1;i<=n;i=j+1,j=i){
		while(j+1<=n&&v[j+1].a==v[i].a) ++j;
		rep(k,i,j) ans1=ans1*(min(v[k].b+1,i)+k-i)%mod;
	}
	for(int i=1,j=1;i<=n;i=j+1,j=i){
		while(j+1<=n&&v[j+1].a==v[i].a) ++j;
		
		s[i-1][0]=1;
		rep(z,1,i-1) s[i-1][z]=s[i-1][z-1];
		
		rep(k,i,j)
		for(int z=0;z<=i-1;++z){
			s[k][z]=z?s[k][z-1]:0;
			if(z<=v[k].b) add(s[k][z],s[k-1][z]);
		}
		ans2=ans2*(s[j][i-1])%mod;
	}
	printf("%d %d
",ans1,ans2);
	return 0;
}
原文地址:https://www.cnblogs.com/yqgAKIOI/p/9802341.html