BZOJ3193 JLOI2013 地形生成

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88363094

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

感觉dp满满的套路。
套路一:
为了满足关键数字的限制,我们容易想到按高度排序,然后从高到低(不能从低到高)枚举当前山峰,我们把前i个山排成一行,然后考虑把第i+1座山插入。这时候就可以处理关键值了,显然你可以插入的位置数就与你的关键值相关(从高到低和从低到高枚举有不同)。
但是有相等高度的山,这样你可以把相同高度的山放在前面,当前山就可以多一个位置放。

套路二:
每一个点的限制是只能放在前k个位置,那么我们将相等高度的点按k的大小排序。后考虑的点的决策区间一定包含前面的点的决策区间,那么你可以放的位置范围一定包含前面的i-1个点,那么你的实际限制范围其实是前k+i-1个位置,直接放就行了。。。。。。

套路三:
对于第二问,我们从低到高枚举(从高到低貌似更简单),每次对于相同高度的山,我们对于剩余位置dp一下再填进去,就行了,这个dp[i][j]表示前i个位置填j个数,因为空的位置是留给更高的山的,可以解决限制。
从高到低枚举,对于相同高度的山,那么就是有k个空去插,因为题目要求我们发现每一种方案都可以通过交换相等高度的值化归为关键数字从小到大的方案,那么就按关键数字从小到大从左到右插入,dp[i][j] = dp[i-1][j] + dp[i-1][j-1] + … dp[i-1][0]。

AC Code:(第二问我是从低到高的)

#include<bits/stdc++.h>
#define maxn 1005
#define mod 2011
using namespace std;

int n,h[maxn],v[maxn],sb[maxn];
vector<int>G[maxn];

inline bool cmp(const int &a,const int &b){ return v[a] < v[b]; }

void add(int &a,int b){ a+=b;a>=mod?a-=mod:0; }

int solve(int n,vector<int>&vec)
{
	static int dp[2][maxn]={};
	int m = vec.size() , now = 1 , pre = 0;
	for(int i=1;i<=m;i++) dp[pre][i] = 0;
	dp[pre][0] = 1;
	for(int i=1;i<=n;i++,swap(now,pre))
	{
		for(int j=0;j<=m;j++) 
			dp[now][j] = 0;
		for(int j=0;j<=m;j++)
		{
			if(dp[pre][j])
			{
				add(dp[now][j],dp[pre][j]);
				if(j<m && i-1<=v[vec[j]]+j)
					add(dp[now][j+1],dp[pre][j]);
			}
		}
	}
	return dp[pre][m];
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&h[i],&v[i]);
		sb[++sb[0]] = h[i] , v[i] --;
	}
	sort(sb+1,sb+1+sb[0]);
	sb[0] = unique(sb+1,sb+1+sb[0])-sb-1;
	for(int i=1;i<=n;i++)
	{
		h[i] = lower_bound(sb+1,sb+1+sb[0],h[i]) - sb;
		G[h[i]].push_back(i);
	}
	
	int ans1 = 1 , had = 1;
	for(int i=sb[0];i>0;i--)
	{
		sort(G[i].begin(),G[i].end(),cmp);
		int tmp = had;
		for(int j=0,siz=G[i].size();j<siz;j++)
		{
			ans1 = 1ll * ans1 * (min(v[G[i][j]]+1+had-tmp,had)) % mod;
			had ++;
		}
	}
	printf("%d ",(ans1+mod)%mod);
	
	int ans2 = 1; had = 0;
	for(int i=1;i<=sb[0];i++)
	{
		ans2 = 1ll * ans2 * solve(n-had,G[i]) % mod , had += G[i].size();
	}
	printf("%d\n",(ans2+mod)%mod);
}
/*
7
1 2
2 2
1 2
1 1
2 3
3 3
3 1
*/

更短的AC Code(第二问从高到低,来自大佬):

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 1005
#define mod 2011
using namespace std;
int n,ans1=1,ans2=1,f[maxn];
struct data{int h,k;}a[maxn];
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;
}
inline bool cmp(data x,data y)
{
	return x.h==y.h?x.k<y.k:x.h>y.h;
}
int main()
{
	int i,j;
	n=read();
	F(i,1,n) a[i].h=read(),a[i].k=read()-1;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i=j)
	{
		memset(f,0,sizeof(f));
		f[0]=1;
		for(j=i;j<=n&&a[i].h==a[j].h;j++)
		{
			ans1=ans1*(min(i,a[j].k+1)+j-i)%mod;
			for(int k=1;k<i&&k<=a[j].k;k++) f[k]=(f[k]+f[k-1])%mod;
		}
		int sum=0;
		for(int k=0;k<i&&k<=a[j-1].k;k++) sum=(sum+f[k])%mod;
		ans2=(ans2*sum)%mod;
	}
	printf("%d %d\n",ans1,ans2);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/88363094