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; }
我是废物
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误