其他分享
首页 > 其他分享> > Codeforces 436D - Pudding Monsters(dp)

Codeforces 436D - Pudding Monsters(dp)

作者:互联网

Codeforces 题目传送门 & 洛谷题目传送门

u1s1 这题数据范围有点迷惑啊……乍一看 \(\mathcal O(nm)\) 过不去,还以为是正解是 \(\mathcal O(n+m^2)\) 呢。

考虑 \(dp\),设 \(f_i\) 表示用前 \(i\) 个布丁,并且第 \(i\) 个布丁要么不动,要么向左移动能够覆盖的最多特殊格子数,再设 \(g_i\) 表示前 \(i\) 个布丁,并且第 \(i\) 个布丁的位置不发生变化所能覆盖的最多特殊格子数。

想好了 \(dp\) 状态,转移就水到渠成了:

当然也可以改写成以下形式:

这样暴力 \(dp\) 是平方的,显然会炸,不过注意到特殊格子数量很少,考虑以此为突破口优化 \(dp\) 转移。比方说拿转移方程 \(f_j+sum(a_i-i+j+1,a_i)\rightarrow g_i\) 举例,注意到 \(f_i\leq f_{i+1}\),而如果 \(a_i-i+j+1\) 不是特殊格子,那么显然 \(sum(a_i-i+j+1,a_i)=sum(a_i-i+j+2,a_i)\),故 \(f_j+sum(a_i-i+j+1,a_i)=f_j+sum(a_i-i+j+2,a_i)\le f_{j+1}+sum(a_i-i+j+2,a_i)\),也就是说我们必定会从 \(a_i-i+j+1\) 为特殊格子的 \(j\) 转移来,故考虑转而枚举特殊格子,这样转移的复杂度就可降到 \(\mathcal O(m)\) 了。

最后,上述转移方程忽略的“连续的布丁会粘在一起”这个条件,其实这也异常容易解决,考虑求出 \(L_i\) 表示布丁 \(i\) 所在的布丁块的左端点,\(R_i\) 表示布丁 \(i\) 所在的布丁块的右端点——这显然可以线性求出。然后直接从对应转移位置的左右端点转移而来即可。可能讲得不是特别清楚,具体见代码罢。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const int MAXM=2e3;
const int MAXPOS=2e5;
int n,m,a[MAXN+5],b[MAXM+5],sum[MAXPOS+5];
int l[MAXN+5],r[MAXN+5],f[MAXN+5],g[MAXN+5];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);sort(a+1,a+n+1);
	for(int i=1;i<=m;i++) scanf("%d",&b[i]),sum[b[i]]++;sort(b+1,b+m+1);
	for(int i=1;i<=MAXPOS;i++) sum[i]+=sum[i-1];
	a[0]=-MAXPOS;a[n+1]=MAXPOS+MAXPOS;
	for(int i=1;i<=n;i++) l[i]=(a[i-1]+1==a[i])?l[i-1]:i;
	for(int i=n;i;i--) r[i]=(a[i+1]-1==a[i])?r[i+1]:i;
	for(int i=1;i<=n;i++){
		f[i]=max(f[i],f[i-1]+sum[a[i]]-sum[a[i]-1]);
		g[i]=max(g[i],f[i-1]+sum[a[i]]-sum[a[i]-1]);
		for(int j=1;j<=m&&b[j]<a[i];j++) if(a[i]-b[j]<i){
			chkmax(g[i],f[l[i-a[i]+b[j]]-1]+sum[a[i]]-sum[b[j]-1]);
		}
		chkmax(f[i],g[i]);
		for(int j=m;j&&b[j]>=a[i];j--) if(b[j]-a[i]<=n-i){
			chkmax(f[r[i+b[j]-a[i]]],g[i]+sum[b[j]]-sum[a[i]]);
		}
	} printf("%d\n",f[n]);
	return 0;
}

标签:格子,布丁,Codeforces,436D,Pudding,rightarrow,sum,dp,define
来源: https://www.cnblogs.com/ET2006/p/Codeforces-436D.html