【bzoj2333 & luoguP3273】棘手的操作(线段树合并)
题目传送门:bzoj2333 luoguP3273
这操作还真“棘手”。。听说这题是可并堆题?然而我不会可并堆。于是我就写了线段数合并,然后调了一晚上,数据结构毁一生!!!QAQ……
其实这题也可以把合并强行看成树上的关系然后dfs序后直接线段树的,然而我菜啊。。看到连边就只能想到线段树合并。
首先用并查集维护图的连通性,然后对于每个连通块建一棵下标为点的编号的线段树,于是:
U=合并两棵树;
A1:单点加;
A2:区间加;
A3:因为是整体加,所以我们可以维护一个delta表示当前每个数被整体加了多少,然后输出时直接加上就好了;
F1:单点查询;
F2:区间查询;
然而还有一个F3整体查询最大值很难处理。于是我再开了一颗线段树,维护每个连通块内的最大值,修改时顺便在上面修改信息,F3操作可以直接在树上查询。
时间复杂度$ O(n \log n) $,然而常数极大。
两颗线段树+奇奇怪怪的维护方法,使写出来的代码膨胀到了3.6kB……然后,调试++,and then,(如果你写的不优美)MLE+TLE。经过了一番痛苦的调试后,终于过了。。。QAQ
又臭又长的代码:
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define maxn 300010
inline ll read()
{
ll x=; char c=getchar(),f=;
for(;c<''||''<c;c=getchar())if(c=='-')f=-;
for(;''<=c&&c<='';c=getchar())x=x*+c-'';
return x*f;
}
struct segment_tree1{
struct point{
int lc,rc,mx,delta;
}sgt[*maxn];
int tot;
void add(int now,int l,int r,int x,int y,int k)
{
if(x<=l&&r<=y)sgt[now].delta+=k,sgt[now].mx+=k;
else{
int mid=(l+r)>>;
if(x<=mid){
if(!sgt[now].lc)sgt[now].lc=++tot;
add(sgt[now].lc,l,mid,x,y,k);
}
if(mid<y){
if(!sgt[now].rc)sgt[now].rc=++tot;
add(sgt[now].rc,mid+,r,x,y,k);
}
sgt[now].mx=std::max(sgt[sgt[now].lc].mx,sgt[sgt[now].rc].mx);
if(sgt[now].mx!=-inf)sgt[now].mx+=sgt[now].delta;
}
}
int getmax(int now,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return sgt[now].mx;
else{
int mid=(l+r)>>,mx=-inf;
if(x<=mid&&sgt[now].lc)mx=std::max(mx,getmax(sgt[now].lc,l,mid,x,y));
if(mid<y&&sgt[now].rc)mx=std::max(mx,getmax(sgt[now].rc,mid+,r,x,y));
return mx+sgt[now].delta;
}
}
void merge(int x,int y,int d)
{
d+=sgt[y].delta-sgt[x].delta;
if(!sgt[x].lc)sgt[x].lc=sgt[y].lc,sgt[sgt[x].lc].delta+=d,sgt[sgt[x].lc].mx+=d;
else if(sgt[y].lc)merge(sgt[x].lc,sgt[y].lc,d);
if(!sgt[x].rc)sgt[x].rc=sgt[y].rc,sgt[sgt[x].rc].delta+=d,sgt[sgt[x].rc].mx+=d;
else if(sgt[y].rc)merge(sgt[x].rc,sgt[y].rc,d);
sgt[x].mx=std::max(sgt[sgt[x].lc].mx,sgt[sgt[x].rc].mx)+sgt[x].delta;
}
}t1;
struct segment_tree2{
struct point{
int mx,delta;
}sgt[*maxn];
void add(int now,int l,int r,int x,int y,int k)
{
if(x<=l&&r<=y)sgt[now].delta+=k,sgt[now].mx+=k;
else{
int mid=(l+r)>>;
if(x<=mid)add(now<<,l,mid,x,y,k);
if(mid<y)add(now<<|,mid+,r,x,y,k);
sgt[now].mx=std::max(sgt[now<<].mx,sgt[now<<|].mx);
if(sgt[now].mx!=-inf)sgt[now].mx+=sgt[now].delta;
}
}
int getmax(int now,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return sgt[now].mx;
else{
int mid=(l+r)>>,mx=-inf;
if(x<=mid)mx=std::max(mx,getmax(now<<,l,mid,x,y));
if(mid<y)mx=std::max(mx,getmax(now<<|,mid+,r,x,y));
return sgt[now].delta+mx;
}
}
}t2;
int rt[maxn],fa[maxn],a[maxn],delta;
int n,m;
char op[];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main()
{
n=read();
t1.tot=; delta=;
t1.sgt[].mx=t2.sgt[].mx=-inf;
for(int i=;i<=n;i++){
a[i]=read();
rt[i]=++t1.tot; fa[i]=i;
t1.add(rt[i],,n,i,i,a[i]);
t2.add(,,n,i,i,a[i]);
}
m=read();
for(int i=;i<=m;i++){
scanf("%s",op);
if(op[]=='U'){
int x=read(),y=read();
x=find(x); y=find(y);
if(x!=y){
fa[y]=x; t1.merge(rt[x],rt[y],);
t2.add(,,n,x,x,std::max(a[x],a[y])-a[x]); t2.add(,,n,y,y,-a[y]-inf);
a[x]=std::max(a[x],a[y]); a[y]=-inf;
}
}
else if(op[]=='A'){
if(op[]==''){
int x=read(),v=read(),fx=find(x);
t1.add(rt[fx],,n,x,x,v);
int t=t1.getmax(rt[fx],,n,,n);
t2.add(,,n,fx,fx,t-a[fx]);
a[fx]=t;
}
else if(op[]==''){
int x=read(),v=read(),fx=find(x);
t1.add(rt[fx],,n,,n,v);
t2.add(,,n,fx,fx,v);
a[fx]+=v;
}
else{
int v=read();
delta+=v;
}
}
else{
if(op[]==''){
int x=read(),fx=find(x);
printf("%d\n",t1.getmax(rt[fx],,n,x,x)+delta);
}
else if(op[]==''){
int x=read(),fx=find(x);
printf("%d\n",a[fx]+delta);
}
else printf("%d\n",t2.getmax(,,n,,n)+delta);
}
}
}
bzoj2333 & luoguP3273
可并堆的做法以后再补吧。。
【bzoj2333 & luoguP3273】棘手的操作(线段树合并)的更多相关文章
- 神奇的操作——线段树合并(例题: BZOJ2212)
什么是线段树合并? 首先你需要动态开点的线段树.(对每个节点维护左儿子.右儿子.存储的数据,然后要修改某儿子所在的区间中的数据的时候再创建该节点.) 考虑这样一个问题: 你现在有两棵权值线段树(大概是 ...
- 2019.01.17 bzoj2333: [SCOI2011]棘手的操作(启发式合并)
传送门 启发式合并菜题. 题意:支持与连通块有关的几种操作. 要求支持连边,单点修改,连通块修改,全局修改和单点查值,连通块查最大值和全局最大值. 我们对每个连通块和答案用可删堆维护最大值,然后用启发 ...
- 【BZOJ2333】棘手的操作(左偏树,STL)
[BZOJ2333]棘手的操作(左偏树,STL) 题面 BZOJ上看把... 题解 正如这题的题号 我只能\(2333\) 神TM棘手的题目... 前面的单点/联通块操作 很显然是一个左偏树+标记 ( ...
- [XJOI NOI2015模拟题13] C 白黑树 【线段树合并】
题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动 ...
- BZOJ2733 [HNOI2012]永无乡 【线段树合并】
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- bzoj 2243 [SDOI2011]染色(树链剖分+线段树合并)
[bzoj2243][SDOI2011]染色 2017年10月20日 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询 ...
- B20J_2733_[HNOI2012]永无乡_权值线段树合并
B20J_2733_[HNOI2012]永无乡_权值线段树合并 Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示.某些 ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并
题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...
随机推荐
- googlr 黄金法则 监控
googlr 黄金法则 监控
- django之页面缓存
一 全站缓存(全局缓存) 在settings中配置,主要就是两个中间件,需要注意的就是两个中间件的前后位置 MIDDLEWARE_CLASSES = ( ‘django.middleware.cac ...
- postman 断言
//断言 pm.test("message等于'操作成功'", function () { var jsonData = pm.response.json(); console.l ...
- Powerdesigner显示列名
设置要修改的列 点击ok即可.
- .........请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping。
今天研究membership的时候出现的问题.在此记录一下. 解决办法就是,将"C:\Program Files (x86)\Microsoft Web Tools\Packages\Asp ...
- win10专业版密钥激活
win10专业版密钥激活 1.查看激活状态,win按键>设置(左下角倒数第二个)>更新和安全>激活---------提示没有有效密钥,需要激活 2.管理员身份打开cmd,搜索框输入c ...
- 简明python教程八----输入/输出
通过创建一个file类的对象来打开一个文件,分别使用file类的read.readline或write方法来读写文件. 最后调用一个close方法来告诉Python我们完成了对文件的使用. poem= ...
- sdut3140 A*B(math)
题目:传送门 题目描述 Your task is to find the minimal positive integer number Q so that the product of digits ...
- matplotlib中的颜色及线条控制
出自 http://www.cnblogs.com/darkknightzh/p/6117528.html 参考网址: http://stackoverflow.com/questions/22408 ...
- HDU 1116 Play on Words(欧拉路径(回路))
http://acm.hdu.edu.cn/showproblem.php?pid=1116 题意:判断n个单词是否可以相连成一条链或一个环,两个单词可以相连的条件是 前一个单词的最后一个字母和后一个 ...