题目链接


\(Description\)

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

  1. 选择一个位置\(i\),令\(A_i=A_i+1\),花费\(B_i\)。必须存在一个位置\(j\),满足\(A_i=A_j,\ i\neq j\),才可以进行。
  2. 选择一个位置\(i\),令\(A_i=A_i-1\),花费\(-B_i\)。必须存在一个位置\(j\),满足\(A_i=A_j+1\),才可以进行。

    你需要对于所有\(i\in[1,n]\),求使得\(A_1,A_2,...,A_i\)两两不同的最小花费是多少。

\(n,A_i\leq2\times10^5,\ 1\leq B_i\leq 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)\times B_i\),也就是所有数的贡献是\(\sum_i A_i'\times B_i-\sum_i A_i\times B_i\)。所以我们只需要在按\(B_i\)排序的过程中维护每个元素新的位置及贡献(其实维护位置就是维护在连续段中的排名,直接插入即可)。

具体就是,对于一个连续段,以\(B_i\)为下标建线段树。最后\(B_i\)是递减的,所以每个区间的贡献就是,\(左区间的\sum B_i\times 右区间的元素个数\)(当然这只是段内相对排名改变的贡献,连续段整体还有 \(连续段左端点\times\sum B_i\)的贡献)。

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

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

注意数组大小要开\(4e5\)!不是\(2e5\)!(值域会扩大)

当你连WA 8遍且发现输出都不一样的时候,基本就是RE了= = 气死我了

ps:似乎不用维护左端点,有右端点有\(size\)不就有左端点了吗。


//483ms	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], sz[x]+=sz[y], sum[x]+=sum[y];// Update(x); 不要写Update...(虽然这题能过)
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\n",Ans);
} return 0;
}

Codeforces.1051G.Distinctification(线段树合并 并查集)的更多相关文章

  1. BZOJ4399魔法少女LJJ——线段树合并+并查集

    题目描述 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味: ...

  2. Educational Codeforces Round 51 (Rated for Div. 2) G. Distinctification(线段树合并 + 并查集)

    题意 给出一个长度为 \(n\) 序列 , 每个位置有 \(a_i , b_i\) 两个参数 , \(b_i\) 互不相同 ,你可以进行任意次如下的两种操作 : 若存在 \(j \not = i\) ...

  3. BZOJ2733[HNOI2012]永无乡——线段树合并+并查集+启发式合并

    题目描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达 ...

  4. 洛谷P3224 [HNOI2012]永无乡(线段树合并+并查集)

    题目描述 永无乡包含 nnn 座岛,编号从 111 到 nnn ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 nnn 座岛排名,名次用 111 到 nnn 来表示.某些岛之间由巨大的桥连接, ...

  5. BZOJ4530 BJOI2014大融合(线段树合并+并查集+dfs序)

    易知所求的是两棵子树大小的乘积.先建出最后所得到的树,求出dfs序和子树大小.之后考虑如何在动态加边过程中维护子树大小.这个可以用树剖比较简单的实现,但还有一种更快更优美的做法就是线段树合并.对每个点 ...

  6. 线段树合并+并查集 || BZOJ 2733: [HNOI2012]永无乡 || Luogu P3224 [HNOI2012]永无乡

    题面:P3224 [HNOI2012]永无乡 题解: 随便写写 代码: #include<cstdio> #include<cstring> #include<iostr ...

  7. 2018.09.30 bzoj4025: 二分图(线段树分治+并查集)

    传送门 线段树分治好题. 这道题实际上有很多不同的做法: cdq分治. lct. - 而我学习了dzyo的线段树分治+并查集写法. 所谓线段树分治就是先把操作分成lognlognlogn个连续不相交的 ...

  8. 【BZOJ2733】永无乡(线段树,并查集)

    [BZOJ2733]永无乡(线段树,并查集) 题面 BZOJ 题解 线段树合并 线段树合并是一个很有趣的姿势 前置技能:动态开点线段树 具体实现:每次合并两棵线段树的时候,假设叫做\(t1,t2\), ...

  9. 洛谷P4121 [WC2005]双面棋盘(线段树套并查集)

    传送门 先膜一下大佬->这里 据说这题正解是LCT,然而感觉还是线段树套并查集的更容易理解 我们对于行与行之间用线段树维护,每一行内用并查集暴力枚举 每一行内用并查集暴力枚举连通块这个应该容易理 ...

随机推荐

  1. Kali linux Nessus &Cracking Password

    1 .Nessus漏洞网站测试(真正体会到什么是专业版和社区版的区别,要技术就不光要勤恳的态度,严谨的思维.还有矢志不渝的志气,还必须要求砸钱,所以狠狠的赚钱才是硬道理),我的半个社区版的很多扫描模块 ...

  2. OrCAD Capture CIS 16.6 导出BOM

    OrCAD Capture CIS 16.6 一.选择设计文件:菜单:Tools > Bill of Materials... 二.Bill of Materials > Open in ...

  3. Centos7上vsftp脚本--> sh vsftp.sh 用户名 密码 --> sh vsftp.sh install

    #!/bin/bash #vsftp install . /etc/rc.d/init.d/functions users=/etc/vsftpd/vftpuser.txt login=/etc/vs ...

  4. Linux系统下inode满了导致无法写文件的解决思路

    解决思路1:删除无用的临时文件,释放inode 进入/tmp目录,执行find -exec命令 find  /tmp  -type  f  -exec  rm  {}  \; 遍历寻找0字节的文件,并 ...

  5. Jmeter测试demo

    复制代码,保存为.jmx文件 需要安装插件: JMeterPlugins-ExtrasLibs E:\软件\apache-jmeter-3.0\lib\ext <?xml version=&qu ...

  6. phoenix表操作

    phoenix表操作 进入命令行,这是sqlline.py 配置到path环境变量的情况下 sqlline.py localhost如果要退出命令行:!q 或者 !quit 3.4.1     创建表 ...

  7. Scrapy 框架 安装

    Scrapy 框架 Scrapy是用纯Python实现一个为了爬取网站数据.提取结构性数据而编写的应用框架,用途非常广泛. 框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页 ...

  8. python练习册0006

    第 0006 题:你有一个目录,放了你一个月的日记,都是 txt,为了避免分词的问题,假设内容都是英文,请统计出你认为每篇日记最重要的词. import re import os def get_li ...

  9. Redis的过期策略和内存淘汰机制

    过期策略 我们set key的时候,都可以给一个expire time,就是过期时间,指定这个key比如说只能存活1个小时,我们自己可以指定缓存到期就失效. 如果假设你设置一个一批key只能存活1个小 ...

  10. 18/03/18 04:53:44 WARN TaskSchedulerImpl: Initial job has not accepted any resources; check your cluster UI to ensure that workers are registered and have sufficient resources

    1:遇到这个问题是在启动bin/spark-shell以后,然后呢,执行spark实现wordcount的例子的时候出现错误了,如: scala> sc.textFile()).reduceBy ...