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;
}

我是废物

posted @ 2024-03-26 18:03  lty_ylzsx  阅读(19)  评论(1编辑  收藏  举报