【NOI.AC R5T2 delete】树状数组前缀max优化DP
利用树状数组进行前缀max dp?orz orz orz
(update:突然发现这其实本质就是一个最长上升子序列,也就是说最长上升子序列也可以这么搞)
题目描述
长度为n的序列A,从中删去恰好k个元素(右边的元素往左边移动),记cnt为新序列中A_{i}=i的元素个数(即权值与下标相同的元素的个数)。求cnt的最大值。输入格式
第一行两个正整数n,k,分别表示序列长度,删去元素的个数。 第二行n个正整数A_{1}..A_{n},描述序列A。输出格式
一行一个整数,表示$cnt$的最大值。样例
input1
8 3 1 1 3 2 4 5 7 5
output1
4
explanation
删掉序列中的第4,7,8个数。input2
见ex_delete2.in。output2
见ex_delete2.out。数据范围和约定
对于20%的数据,n20。 对于40%的数据,n500。 对于60%的数据,n5000。 对于80%的数据,n100000。 对于100%的数据,n1000000,Ai<=1e9,k<=n。 时间限制:1s 空间限制:512MB样例下载
样例下载 愉快地又考爆炸了,直接40名开外,想到自己这么弱,压力巨大orz 考场上只写出了n^2的DP,勉强混了60分了,结果和题解上的60分做法相比,弱了不止一点orz最后还一直在尝试优化60分的方程orz orz orz 以下蒟蒻版60暴力#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn = 5005;
int n,k;
int a[maxn];
int ff[maxn*2];
int dp[maxn][maxn];
int ma[maxn];
int main() {
// freopen("ex_delete2.in","r",stdin);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i] = i-a[i];
if(a[i]>=0&&a[i]<=k) ff[a[i]]++;
}
int ans = 0;
for(int i=1;i<=n;i++) {
if(a[i]>=0&&a[i]<=k) ff[a[i]]--;
for(int j=0;j<=min(i,k);j++) {
dp[i][j] = dp[i-1][j]+(a[i]==j);
if(j>0)//shan
dp[i][j] = max(dp[i][j],dp[i-1][j-1]);
}
ans = max(ans,dp[i][k]+ff[k]);
}
printf("%d",ans);
}
dp[i][j]表示到第i个位置删除了j个数可以得到[1...i]的最大cnt数,然后删除仅仅在前i个删除,后面的就因为删除数固定cnt数也固定,处理删j个数时的后面cnt为ff[j]。然后这个方程也很好转移。dp[i][j] = max( dp[i-1],dp[i-1][j-1]), ans = max(ans,dp[i][k]+ff[k]),然后死活优化不出来orz orz orz 太弱了。
正解:
dp[i]表示,在选取i这个数进入cnt,可以得到的最大cnt数。由于一定要选取i这个数,所以前面要删除几个数已经固定好了,考虑转移dp[i] = dp[j]+1(j<i且ai-aj<=i-j,就是将i到j之间的某些数删除然后得到i的正确位置),我们将转移条件移一下项得到j-aj<=i-ai。那么我们先选出可以被选入cnt的数(i-a[i]>=0&&i-a[i]<=k,a[i]<=n-k(如果<0或者a[i]>n-k这辈子不可能归位,如果>k说明需要超过k次消除才能归位))以i-a[i]作为关键字排序之后,对于i,找到在他前面且比他小的数转移就可以了(n^2)。
由于我们已经消除掉了一维,剩下的一维就是大小。这个我们可以用魔改过的树状数组处理,就可以找到"前缀最大值(位置在前面,且数字比他小,dp的最大值)"转移+1就ok了,最后代码写出来贼简短。
魔改树状数组,原来虽然听OB说过,但从来没用过orz orz orz。就是将原本add修改为取max,原本getsum改成取max。这样就可以在线支持求前缀最大值了。
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define lowbit(x) ((x)&(-x))
#define pr pair<int,int>
using namespace std;
const int maxn = 1000005;
int n,k;
int bit[maxn];
void add(int x,int s) {
for(;x<=n+1;x+=lowbit(x)) bit[x] = max(bit[x],s);
}
int getmax(int x) {
int s=0; for(;x;x-=lowbit(x))s=max(s,bit[x]); return s;
}
int tot;
pr z[maxn];
int main() {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) {
int x; scanf("%d",&x);
if(i-x>=0&&i-x<=k&&x<=n-k) z[++tot] = pr(i-x,x);
}
sort(z+1,z+1+tot);
for(int i=1;i<=tot;i++) {
int s = getmax(z[i].second-1);
add(z[i].second,s+1);
}
printf("%d",getmax(n));
}

浙公网安备 33010602011771号