Codeforces.1051G.Distinctification(线段树合并 并查集)

Posted sovietpower

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces.1051G.Distinctification(线段树合并 并查集)相关的知识,希望对你有一定的参考价值。

题目链接


(Description)

给定(n)个数对(A_i,B_i)。你可以进行任意次以下两种操作:

  1. 选择一个位置(i),令(A_i=A_i+1),花费(B_i)。必须存在一个位置(j),满足(A_i=A_j, i eq j),才可以进行。
  2. 选择一个位置(i),令(A_i=A_i-1),花费(-B_i)。必须存在一个位置(j),满足(A_i=A_j+1),才可以进行。
    你需要对于所有(iin[1,n]),求使得(A_1,A_2,...,A_i)两两不同的最小花费是多少。

(n,A_ileq2 imes10^5, 1leq B_ileq n且互不相同)

(Solution)

考虑如果(A_i)互不相同,若存在(A_i+1=A_j),则可以交换(A_i,A_j),花费为(B_i-B_j)。所以最后序列会被分成(A_i)连续的若干段,且每段(B_i)递减。
如果(A_i)有可能相同,可以先把(A_i)变成互不相同(把相同的位置上的数移到该连续段的右端点(+1)处,并查集维护),再进行同样的操作。

设数(A_i)最终变成了(A_i')(按(B_i)排序后被放到了(A_i')位置去),我们发现(i)的贡献就是((A_i'-A_i) imes B_i),也就是所有数的贡献是(sum_i A_i' imes B_i-sum_i A_i imes B_i)。所以我们只需要在按(B_i)排序的过程中维护每个元素新的位置及贡献(其实维护位置就是维护在连续段中的排名,直接插入即可)。
具体就是,对于一个连续段,以(B_i)为下标建线段树。最后(B_i)是递减的,所以每个区间的贡献就是,(左区间的sum B_i imes 右区间的元素个数)(当然这只是段内相对排名改变的贡献,连续段整体还有 (连续段左端点 imessum B_i)的贡献)。

加入一个(A_i)时,可能把两个连续段合并在一起。把原先两个连续段的贡献减掉,然后线段树合并两段,再加上合并后的段的贡献即可。

复杂度(O(nlog n))(为啥(Tutorial)上写的是(O(nlog^2n))...?没细看...)。

注意数组大小要开(4e5)!不是(2e5)!(值域会扩大)
当你连WA 8遍且发现输出都不一样的时候,基本就是RE了= = 气死我了


//499ms 161100KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=4e5+3;//4e5 not 2e5!

int fa[N],R[N],root[N];
LL Ans;
struct Segment_Tree
{
    #define ls son[x][0]
    #define rs son[x][1]
    #define lson ls,l,m
    #define rson rs,m+1,r
    #define S N*20
    int tot,sz[S],son[S][2];
    LL sum[S];
    #undef S
    #define Update(x) sz[x]=sz[ls]+sz[rs], sum[x]=sum[ls]+sum[rs]
    void Insert(int &x,int l,int r,int pos)
    {
        if(!x) x=++tot;
        if(l==r) {sz[x]=1, sum[x]=pos; return;}
        int m=l+r>>1;
        pos<=m ? Insert(lson,pos) : Insert(rson,pos);
        Update(x);
    }
    int Merge(int x,int y)
    {
        if(!x||!y) return x|y;
        Ans-=sum[ls]*sz[rs]+sum[son[y][0]]*sz[son[y][1]];
        ls=Merge(ls,son[y][0]), rs=Merge(rs,son[y][1]);
        Ans+=sum[ls]*sz[rs], Update(x);
        return x;
    }
}T;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now;
}
int Find(int x)
{
    return x==fa[x]?x:fa[x]=Find(fa[x]);
}
void Merge(int x,int y)
{
    x=Find(x), y=Find(y), fa[y]=x;
    Ans-=T.sum[root[x]]*x+T.sum[root[y]]*y;
    root[x]=T.Merge(root[x],root[y]);
    Ans+=T.sum[root[x]]*x;
    R[x]=R[y];
}

int main()
{
    const int n=read();
    for(int i=1; i<N; ++i) R[i]=i, fa[i]=i;
    for(int i=1; i<=n; ++i)
    {
        int a=read(),b=read(),p=root[a]?R[Find(a)]+1:a;
        Ans-=1ll*a*b, T.Insert(root[p],1,n,b), Ans+=1ll*p*b;
        if(root[p-1]) Merge(p-1,p);
        if(root[p+1]) Merge(p,p+1);
        printf("%I64d
",Ans);
    }

    return 0;
}

以上是关于Codeforces.1051G.Distinctification(线段树合并 并查集)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces 1051 D.Bicolorings(DP)

Vasya And Password(CodeForces - 1051A)

[Codeforces 1051F] The Shortest Statement 解题报告(树+最短路)

The Shortest Statement CodeForces - 1051F(待测试)

CodeForces 1051d:连通块 DP

Codeforces.1051G.Distinctification(线段树合并 并查集)