Codeforces 436D - Pudding Monsters(dp)
作者:互联网
u1s1 这题数据范围有点迷惑啊……乍一看 \(\mathcal O(nm)\) 过不去,还以为是正解是 \(\mathcal O(n+m^2)\) 呢。
考虑 \(dp\),设 \(f_i\) 表示用前 \(i\) 个布丁,并且第 \(i\) 个布丁要么不动,要么向左移动能够覆盖的最多特殊格子数,再设 \(g_i\) 表示前 \(i\) 个布丁,并且第 \(i\) 个布丁的位置不发生变化所能覆盖的最多特殊格子数。
想好了 \(dp\) 状态,转移就水到渠成了:
- 对于 \(f_i\),要么第 \(i\) 个布丁不动,即 \(g_i\rightarrow f_i\),要么第 \(i\) 个布丁向左移动,那么我们就枚举上一个不动的布丁 \(j\),\(g_j+sum(a_j+1,a_j+i-j)\rightarrow f_i\),其中 \(sum(l,r)\) 表示 \([l,r]\) 中特殊格子的个数。
- 对于 \(g_i\),枚举上一个不动或向左移动的布丁 \(j\),那么显然布丁 \(j+1,j+2,\dots,i-1\) 都会向右移动直到靠到 \(i\) 上,即 \(f_j+sum(a_i-i+j+1,a_i)\rightarrow g_i\)。
当然也可以改写成以下形式:
- \(f_j+sum(a_i-i+j+1,a_i)\rightarrow g_i\)
- \(g_i+sum(a_i+1,a_i+j)\rightarrow f_{i+j}\)
- \(g_i\rightarrow f_i\)
这样暴力 \(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