题解 CF1051G【Distinctification】

$\text{Link}$

题意

给你两个长为 $n$ 的序列 $a$ 和 $b$,你可以进行两种操作若干次:

  1. 若有 $i\ne j$ 且 $a_i=a_j$,则可以令 $a_i\gets a_i+1$,代价为 $b_i$。
  2. 若有 $a_i=a_j+1$,则可以令 $a_i\gets a_i-1$,代价为 $-b_i$。

对于 $\forall i\in[1,n]$,求出最小的代价使得 $a_{1\dots i}$ 互不相同。

$1\le n,a_i\le 2\times 10^5$,$b$ 为一个 $[1,n]$ 的排列。

思路

最终的代价只与 $a_i$ 最终的值有关。

在一个值域连续段中,$a_i$ 的值无论如何操作都无法小于最小值,所以只是将 $a_i$ 或 $b_i$ 重新排序使得答案最小的问题。

可以发现,若 $a_i=a_j+1$,则可以交换 $a_i$,$a_j$,并花费 $b_j-b_i$ 的代价。所以如果 $a_i$ 在一个值域上的连续段中,则可以将 $b_i$ 降序排序,使得代价最小。

我们需要维护每个值域连续段并支持合并,考虑线段树合并。

对每个值域连续段的左端点建立权值线段树,以 $b_i$ 为下标,考虑到可以视为将小于 $mid$ 的值整体右移,答案为 $sum_{rt}\times l+\displaystyle\sum sum_{ls}\times siz_{rs}$。

用并查集找到连续段左端点并记录相应右端点。

数组记得开两倍。

时间复杂度 $O(n\log n)$。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=4e5+10;
int n,f[N],br[N];
ll ans;
inline int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}
int rt[N];
struct Segument_Tree{
    int son[2][N*60],cnt;
    int siz[N*60];
    ll sum[N*60];
    #define ls (son[0][rt])
    #define rs (son[1][rt])
    inline void pushup(int rt){
        sum[rt]=sum[ls]+sum[rs];
        siz[rt]=siz[ls]+siz[rs];
    }
    inline void insert(int &rt,int l,int r,int p){
        if(!rt)
            rt=++cnt;
        if(l==r){
            sum[rt]=p,siz[rt]=1;
            return ;
        }
        int mid=l+r>>1;
        if(p<=mid) insert(ls,l,mid,p);
        else insert(rs,mid+1,r,p);
        pushup(rt);
    }
    inline int merge(int a,int b,int l,int r){
        if(!a||!b) return a+b;
        int rt=++cnt;
        if(l==r) return rt;
        ans-=sum[son[0][a]]*siz[son[1][a]]+
            sum[son[0][b]]*siz[son[1][b]];
        int mid=l+r>>1;
        ls=merge(son[0][a],son[0][b],l,mid);
        rs=merge(son[1][a],son[1][b],mid+1,r);
        ans+=sum[ls]*siz[rs];
        pushup(rt);
        return rt;
    }
}t;
inline void solve(int u,int v){
    u=find(u),v=find(v),f[v]=u;
    ans-=t.sum[rt[u]]*u+t.sum[rt[v]]*v;
    rt[u]=t.merge(rt[u],rt[v],1,n);
    ans+=t.sum[rt[u]]*u;
    br[u]=br[v];
}
int main(){
    n=read();
    for(int i=1;i<=N-10;i++)
        f[i]=br[i]=i;
    for(int i=1;i<=n;i++){
        int p=read(),v=read();
        int xp=rt[p]?br[find(p)]+1:p;
        ans+=1ll*(xp-p)*v;
        t.insert(rt[xp],1,n,v);
        if(rt[xp-1]) solve(xp-1,xp);
        if(rt[xp+1]) solve(xp,xp+1);
        write(ans),putc('\n');
    }
    flush();
}

再见 qwq~

posted @ 2022-08-02 09:17  ffffyc  阅读(9)  评论(0)    收藏  举报  来源