树状数组的一些题

【线上训练 19】序列

(http://zhengruioi.com/problem/1150)

sol:

树状数组优化的 \(DP\) 往往是从暴力 \(DP\) 启发而来的。考虑 \(O(n^2)\) 暴力,只要算出原序列的代价,然后枚举尝试修改每一个点,有一个显然的性质就是我们必须把那个数修改成一个原序列出现过的数。考虑优化,会发现一共有三种情况:(假设现在要把 \(p_i\) 变成 \(p_j\)\(nxt_i\) 表示 \(p_i\) 下一次出现的位置,若没有就置为 \(n+1\)
1、\(j<i\) ,这时的修改一定不会对后面的贡献产生影响,所以只要在 \(i\) 前面找出 \(|p_i-p_j|\) 最小的即可,考虑用 \(set\) 维护。
2、\(i<j<nxt_i\) ,手玩一下发现在 \([j,nxt_i)\) 这一段数字出现次数少了 \(1\),这就意味着这种情况可能出现更优的解。此时新增的代价为 \(|a_i−a_j |-S(j)+S(nxt_i)\),其中 \(S(k)=\sum^{n}_{i=k} i\)。然后我们还要拆开绝对值分两种情况讨论:
I. \(a_i>a_j\)\(ans=\min \{a_i+S(nxt_i)-a_j-S(j) \}\),然后发现只要用树状数组维护出 \(-a_j-S(j)\) 的最小值就行了,查询的话注意只能查比 \(a_i\) 小的情况。
II. \(a_i<a_j\)\(ans=\min \{-a_i+S(nxt_i)+a_j-S(j) \}\),用树状数组维护出 \(a_j-S(j)\) 的最小值,查询同理。
3、\(nxt_i<j\) ,继续手玩一下发现在 \([nxt_i,j)\) 这一段数字出现次数多了 \(1\),那么这种情况不可能出现更优的解了,直接舍去。
总复杂度 \(O(n\log n)\)
吐槽:这分类讨论怎么做着感觉就像是在做高一的数学题?(而且错一个就爆零)

const int N=500005;
int n,p[N],tmp[N],q[N],nxt[N],h[N];
ll ans,as;
struct bit{
    ll st[N];
    inline int lowbit(int x){return x&-x;}
    inline void inst(int x,ll y){while(x) st[x]=min(st[x],y),x-=lowbit(x);}
    inline ll query(int x){ll res=1e18;while(x<=n) res=min(res,st[x]),x+=lowbit(x);return res;}
} t1,t2;
inline ll calc(int x){return 1ll*(n-x+1)*(n+x)/2;}
set<int> s;
set<int>::iterator it;
int main(){
    read(n);
    for(int i=1;i<=n;i++) read(p[i]),tmp[i]=p[i];
    sort(tmp+1,tmp+1+n);
    int k=unique(tmp+1,tmp+1+n)-tmp-1;
    for(int i=1;i<=n;i++) q[i]=lower_bound(tmp+1,tmp+1+k,p[i])-tmp;
    for(int i=1;i<=n;i++){
        if(h[q[i]]) nxt[h[q[i]]]=i;
        h[q[i]]=i;
    }
    for(int i=1;i<=n;i++){
        if(h[q[i]]) nxt[h[q[i]]]=n+1;
        h[q[i]]=0;
    }
    int num=0;
    for(int i=1;i<=n;i++){
        if(!h[q[i]]) h[q[i]]=1,num++;
        ans+=1ll*num*i;
    }as=ans;
    memset(h,0,sizeof(h));
    for(int i=1;i<=n;i++){
        if(h[q[i]]) continue;
        h[q[i]]=i;ll val=calc(i)-calc(nxt[i]);
        it=s.lower_bound(p[i]);
        if(it!=s.end()) ans=min(ans,as-val+*it-p[i]);
        if(it!=s.begin()) ans=min(ans,as-val+p[i]-*(--it));
        s.insert(p[i]);
    }
    memset(t1.st,0x3f,sizeof(t1.st));
    memset(t2.st,0x3f,sizeof(t2.st));
    for(int i=n;i>0;i--){
        if(h[q[i]]!=i) continue;
        ans=min(ans,as+t1.query(n-q[i]+1)+calc(nxt[i])+p[i]);
        ans=min(ans,as+t2.query(q[i])+calc(nxt[i])-p[i]);
        t1.inst(n-q[i]+1,-calc(i)-p[i]);
        t2.inst(q[i],-calc(i)+p[i]);
    }
    print(ans);
    return 0;
}


猜你喜欢

转载自www.cnblogs.com/zxynothing/p/11838832.html