HZOI初三奥赛模拟测试3-T2
\(HZOI\)初三奥赛模拟测试\(3-T2\)
题目描述
给定序列 \(a_1,a_2,\dots a_n\) ,现在可以选择删除一些数,使得删除后 \(\sum [a_i=i]\) 最大
做题历程
一下午就做了这一个题,打到最后才发现时间复杂度 \(O(\frac{n^2}{2})\) 过不去,没时间优化了,最后 \(73pts\),赛时最高,好像因为我多剪了一个小枝。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define read read()
#define pt puts("")
inline int read
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return f*x;
}
void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
return;
}
int n;
const int N=5*1e5+10;
int a[N];
int dis[N];
int dp[N];
int first[N];
int m;
int t[N];
int ans;
void LSH()
{
for(int i=1;i<=n;i++) t[i]=a[i];
sort(t+1,t+n+1);
m=unique(t+1,t+n+1)-t-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(t+1,t+m+1,a[i])-t;
return;
}
signed main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
n=read;
for(int i=1;i<=n;i++){
a[i]=read;
dis[i]=i-a[i];
}
LSH();
for(int i=1;i<=n;i++){
if(dis[i]>=0){//就是这,不让没可能的数跑
for(int j=i-1;j>=1;j--){
if(a[j]<a[i])
if(dp[j]>dp[i])
if((t[a[i]]-t[a[j]]-1)<=(i-j-1))
dp[i]=dp[j];
}
dp[i]++;
}
ans=max(ans,dp[i]);
// cout<<"dp["<<i<<"]="<<dp[i]<<'\n';
}
write(ans);pt;
return 0;
}
正解
我比较 \(cai\),所以用的是@hs_mo的 树状数组优化DP
,官方题解的神做法我显然是不会的 $\dots $
分析这个\(DP\),式子很好推:
\[dp_i=\max {dp_j} +1
\]
而 \(j\) 要满足:
\[j<i
\]
\[a_j<a_i
\]
\[a_i-a_j \leq i-j \ 即\ a_i-i \leq a_j-j
\]
我们发现,当满足下面两条性质时,一定有 \(j<i\)
那么我们就先给 \(a\) 数组排序,这样可以保证树状数组往前查找时 \(a_j<a_i\) ;然后定义数组 \(c\), 令\(c_i=a_i-i\) ,再对 \(c\) 排序,那么处理当前 \(i\) 时,前面处理过的 \(c_j\),必然都大于 \(c_i\)。
然后就可以写出树状数组了,挺绕的,不画图看不出来
\(AC\ \ code\)
#include<bits/stdc++.h>
using namespace std;
#define lowbit(i) (i&(-i))
#define int long long
#define read read()
#define pt puts("")
inline int read
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return f*x;
}
void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
return;
}
int n;
const int N=5*1e5+10;
int dis[N];
int dp[N];
int first[N];
int m;
int t[N];
int ans;
struct Node{
int id;
int val;
}a[N];
struct C{
int dis;
int id;
}c[N];
bool cmp1(Node A,Node B)
{
if(A.val==B.val) return A.id<B.id;
return A.val<B.val;
}
bool cmp2(C A,C B)
{
if(A.dis==B.dis) return A.id<B.id;
return A.dis>B.dis;
}
int tr[N];
void add(int x,int k)
{
for(int i=x;i<=n;i+=lowbit(i)){
tr[i]=max(tr[i],k);
}
}
int query(int x)
{
int res=0;
for(int i=x;i>0;i-=lowbit(i)){
res=max(res,tr[i]);
}
return res;
}
bool f[N];
signed main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
n=read;
for(int i=1;i<=n;i++){
a[i].val=read;
a[i].id=i;
}
sort(a+1,a+n+1,cmp1);
for(int i=1;i<=n;i++){
c[i].dis=a[i].val-a[i].id;
c[i].id=i;
}
sort(c+1,c+n+1,cmp2);
for(int i=1;i<=n;i++)
{
if(a[i].val==a[i-1].val) first[i]=first[i-1];
else first[i]=i;//因为我们要处理a[j]<a[i]的,所以要把等于它的跳过
}
for(int cc=1;cc<=n;cc++){
if(c[cc].dis>0) continue;
int i=c[cc].id;
dp[i]=query(first[i]-1)+1;
add(i,dp[i]);
ans=max(ans,dp[i]);
}
write(ans);pt;
return 0;
}
我是废物