zoukankan      html  css  js  c++  java
  • CF 1051 G. Distinctification

    G. Distinctification

    链接

    分析:

      线段树合并 + 并查集。

      最后操作完后a连续递增的一段,b一定是递减的。最后的答案是$sum (a_{new}-a_{odd}) imes b_i$,即改变后的a减去之前的a。

      那么对于连续的一段考虑怎么求。按照bi建立权值线段树,线段树的一个节点的答案就是 左区间的答案+右区间的答案+左区间的和 × 右区间的个数。

      即最大的$b_i$乘1,次大的乘2,...,最小的乘(n-1)分别是每个$b_i$的排名。这是对b排序后,新的答案,减去以前的即可得到答案。

      如果存在相同的a,那么让a变成a+1,存在a+1,那么变成a+2……加上变后的贡献,并查集维护。注意扩大值域后,开两倍空间。

    代码:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<cctype>
    #include<set>
    #include<vector>
    #include<queue>
    #include<map>
    #define fi(s) freopen(s,"r",stdin);
    #define fo(s) freopen(s,"w",stdout);
    using namespace std;
    typedef long long LL;
    
    inline int read() {
        int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
        for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
    }
    
    const int N = 400005;
    int ls[N * 20], rs[N * 20], siz[N * 20], Root[N], R[N], fa[N], Index;
    LL sum[N * 20], Ans;
    
    void Insert(int l,int r,int &now,int p) {
        if (!now) now = ++Index;
        if (l == r) { siz[now] = 1, sum[now] = p; return ; }
        int mid = (l + r) >> 1;
        if (p <= mid) Insert(l, mid, ls[now], p);
        else Insert(mid + 1, r, rs[now], p);
        sum[now] = sum[ls[now]] + sum[rs[now]], siz[now] = siz[ls[now]] + siz[rs[now]];
    }
    int Merge(int x,int y) {
        if (!x || !y) return x | y;
        Ans -= sum[ls[x]] * siz[rs[x]] + sum[ls[y]] * siz[rs[y]];
        ls[x] = Merge(ls[x], ls[y]);
        rs[x] = Merge(rs[x], rs[y]);    
        Ans += sum[ls[x]] * siz[rs[x]], siz[x] += siz[y], sum[x] += sum[y];
        return x;
    }
    int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
    void solve(int x,int y) {
        x = find(x), y = find(y); fa[y] = x;
        Ans -= sum[Root[x]] * x + sum[Root[y]] * y;
        Root[x] = Merge(Root[x], Root[y]);
        Ans += sum[Root[x]] * x;
        R[x] = R[y];
    }
    int main() {
        int n = read();
        for (int i = 1; i <= 400000; ++i) fa[i] = i, R[i] = i;
        for (int i = 1; i <= n; ++i) {
            int a = read(), b  = read();
            int p = Root[a] ? R[find(a)] + 1 : a;
            Ans -= 1ll * a * b;
            Insert(1, n, Root[p], b);
            Ans += 1ll * p * b;
            if (Root[p - 1]) solve(p - 1, p);
            if (Root[p + 1]) solve(p, p + 1);
            printf("%I64d
    ", Ans);
        }    
        return 0;
    }
  • 相关阅读:
    linux环境基于python语言docx转pdf
    python pip install XXX出现报错问题
    最干净的pyinstaller打包成exe应用程序方法
    python pyinstaller 打包程序报错解决
    sklearn中predict()与predict_proba()用法区别
    机器学习(数据挖掘十个重要算法)
    在后台管理器上互动,获取后台输入的信息。
    求数组里面最大值,最小值
    数组的排序
    冒泡法的实例(给数组排序)
  • 原文地址:https://www.cnblogs.com/mjtcn/p/10406703.html
Copyright © 2011-2022 走看看