题目链接:棘手的操作

  网上的题解大部分都是在线用可并堆艹……但是树高严格\(\log\)的可并堆我不会啊……还是离线大法好……

  我们可以先把所有的合并操作用并查集给处理好,把得到的森林记录下来。然后,我们对这个森林进行\(dfs\),就可以得到一个\(dfs\)序,那么我们把所有点按照\(dfs\)序重标号,每个联通块就成为了一段区间了。然后就可以直接用线段树维护了。

  注意一个细节:在\(dfs\)的时候对于一个点连出去的所有边,要优先走先连的边,这样才能保证联通块始终是一段区间。

  下面贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 300010
#define INF 2147483647using namespace std;
typedef long long llg;struct data{int op,x,y;
}s[maxn];
int head[maxn],next[maxn],to[maxn],tt,du[maxn];
int fa[maxn],n,m,a[maxn],le[maxn],ri[maxn],b[maxn];
int maxv[maxn<<2],addv[maxn<<2],L,R,z,_max,_add;
char ss[20];int getint(){int w=0;bool q=0;char c=getchar();while((c>'9'||c<'0')&&c!='-') c=getchar();if(c=='-') c=getchar(),q=1;while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();return q?-w:w;
}int find(int x){return fa[fa[x]]==fa[x]?fa[x]:fa[x]=find(fa[x]);}
void link(int x,int y){du[x]++;to[++tt]=y;next[tt]=head[x];head[x]=tt;}
void dfs(int u){le[u]=++tt;int *d=new int[du[u]];for(int i=head[u],j=0;i;i=next[i]) d[j++]=to[i];for(int i=du[u];i;i--) dfs(d[i-1]);
}void build(int u,int l,int r){int lc=u<<1,lv=u<<1|1,mid=(l+r)>>1;if(l==r){maxv[u]=b[l];return;}build(lc,l,mid); build(lv,mid+1,r);maxv[u]=max(maxv[lc],maxv[lv]);
}void add(int u,int l,int r){int lc=u<<1,lv=u<<1|1,mid=(l+r)>>1;if(l>=L && r<=R){maxv[u]+=z,addv[u]+=z;return;}if(L<=mid) add(lc,l,mid);if(R>mid) add(lv,mid+1,r);maxv[u]=max(maxv[lc],maxv[lv])+addv[u];
}void query(int u,int l,int r){int lc=u<<1,lv=u<<1|1,mid=(l+r)>>1;if(l>=L && r<=R){_max=max(_max,maxv[u]+_add);return;}_add+=addv[u];if(L<=mid) query(lc,l,mid);if(R>mid) query(lv,mid+1,r);_add-=addv[u];
}void work(){_max=-INF; query(1,1,n);printf("%d\n",_max);
}int main(){File("a");n=getint();for(int i=1;i<=n;i++) a[i]=getint(),fa[i]=i;m=getint();for(int i=1,u,v;i<=m;i++){scanf("%s",ss); if(!ss[1]) ss[1]='1';s[i].op=(ss[0]=='A')+(ss[0]=='F')*4+ss[1]-'0';if(s[i].op<7) s[i].x=getint();if(s[i].op<4) s[i].y=getint();if(s[i].op==1){u=find(s[i].x),v=find(s[i].y);if(u!=v) fa[u]=v,link(v,u);}}tt=0;for(int i=1;i<=n;i++) if(find(i)==i) dfs(i);for(int i=1;i<=n;i++) b[le[i]]=a[i],ri[i]=le[i];for(int i=1;i<=n;i++) fa[i]=i; build(1,1,n);for(int i=1,u,v;i<=m;i++){u=s[i].x; z=v=s[i].y;if(s[i].op==1){u=find(u),v=find(v);if(u!=v) fa[u]=v,ri[v]=ri[u];}else if(s[i].op==2) L=R=le[u],add(1,1,n);else if(s[i].op==3) u=find(u),L=le[u],R=ri[u],add(1,1,n);else if(s[i].op==4) z=u,L=1,R=n,add(1,1,n);else if(s[i].op==5) L=R=le[u],work();else if(s[i].op==6) u=find(u),L=le[u],R=ri[u],work();else printf("%d\n",maxv[1]);}return 0;
}

  UPD 3.2:左偏树做法

  其实无须树高严格\(\log\),左偏树就够了

  网上有的题解是每次用\(O(树高)\)的时间统计影响这个点的所有标记,可并堆用的是左偏树= =

  但是这样复杂度是不对的,因为左偏树的树高可以达到\(O(n)\)级别

  然后就需要考虑一种别的解法

  既然不能每次暴力统计到根的所有标记,我们可以考虑把标记永久化了,固定在根节点,这样每次就只需要查询根节点的标记就可以了。但是这样的话合并的时候会出问题,两个堆无法直接合并。不要慌,我们只需要把\(size\)较小的那个堆里面所有的元素暴力修改掉就可以直接合并了。再用个全局的堆维护一下全局最大值。总复杂度\(O(n\log n)\)。

  顺便Orz告诉我此解法的xlightgod大爷Orz

  PS:我写的是斜堆,不是左偏树

  下面贴代码:

#include<bits/stdc++.h>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 300010using namespace std;
typedef long long llg;struct Queue{priority_queue<int> q1,q2;void insert(int x){q1.push(x);}void erase(int x){q2.push(x);}int top(){while(!q2.empty() && q1.top()==q2.top()) q1.pop(),q2.pop();return q1.top();}
}q;
int n,rt[maxn],siz[maxn],fa[maxn],addv[maxn];
int ff[maxn],s[maxn][2],val[maxn],m,z,_add;
char ss[20];int getint(){int w=0;bool q=0;char c=getchar();while((c>'9'||c<'0')&&c!='-') c=getchar();if(c=='-') c=getchar(),q=1;while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();return q?-w:w;
}int find(int x){return ff[ff[x]]==ff[x]?ff[x]:ff[x]=find(ff[x]);}
int merge(int u,int v){if(!u || !v) return u+v;if(val[u]<val[v]) swap(u,v);fa[s[u][1]=merge(s[u][1],v)]=u;swap(s[u][0],s[u][1]); return u;
}void del(int u){int x=find(u),y=fa[u];if(rt[x]==u) fa[rt[x]=merge(s[u][0],s[u][1])]=0;else fa[s[y][u==s[y][1]]=merge(s[u][0],s[u][1])]=y;
}void dfs(int u){val[u]+=z;if(s[u][0]) dfs(s[u][0]);if(s[u][1]) dfs(s[u][1]);
}int main(){File("a");n=getint();for(int i=1;i<=n;i++){ff[i]=rt[i]=i,siz[i]=1;q.insert(val[i]=getint());}m=getint();while(m--){int x,y,u;scanf("%s",ss+1);if(ss[1]=='U'){x=find(getint()),y=find(getint());if(siz[x]>siz[y]) swap(x,y);if(x!=y){q.erase(min(val[rt[x]]+addv[x],val[rt[y]]+addv[y]));z=addv[x]-addv[y],dfs(rt[x]); siz[y]+=siz[x];ff[x]=y; rt[y]=merge(rt[x],rt[y]);}}else if(ss[1]=='A'){x=getint();if(ss[2]=='1' || ss[2]=='2'){u=find(x); y=getint();q.erase(val[rt[u]]+addv[u]);if(ss[2]=='1'){del(x); val[x]+=y;s[x][0]=s[x][1]=fa[x]=0;rt[u]=merge(rt[u],x);}else addv[u]+=y;q.insert(val[rt[u]]+addv[u]);}else if(ss[2]=='3') _add+=x;}else{if(ss[2]=='3') printf("%d\n",q.top()+_add);else{x=getint(); u=find(x);if(ss[2]=='1') printf("%d\n",val[x]+addv[u]+_add);else if(ss[2]=='2') printf("%d\n",val[rt[u]]+addv[u]+_add);}}}return 0;
}

转载于:https://www.cnblogs.com/lcf-2000/p/6484275.html

BZOJ 2333 【SCOI2011】 棘手的操作相关推荐

  1. 【BZOJ】2333: [SCOI2011]棘手的操作

    http://www.lydsy.com/JudgeOnline/problem.php?id=2333 题意: 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i], ...

  2. 洛谷P3273 [SCOI2011] 棘手的操作 [左偏树]

    题目传送门 棘手的操作 题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 ...

  3. 【左偏树】【bzoj 2333】: [SCOI2011]棘手的操作

    http://www.lydsy.com/JudgeOnline/problem.php?id=2333 带lazy的左偏树,由于我不会写,所以借(chao)鉴(xi)了一下hzwer #includ ...

  4. [bzoj2333] [SCOI2011]棘手的操作 (可并堆)

    //以后为了凑字数还是把题面搬上来吧2333 发布时间果然各种应景... Time Limit: 10 Sec  Memory Limit: 128 MB Description 有N个节点,标号从1 ...

  5. bzoj2333 [SCOI2011]棘手的操作(洛谷3273)

    题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作:U x y: 加一条边,连接第x个节点和第y个节点A1 x v: 将第x个节点的权 ...

  6. BZOJ2333 [SCOI2011]棘手的操作 【离线 + 线段树】

    题目 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权 ...

  7. bzoj2333[SCOI2011]棘手的操作

    可以大力写一个平衡树启发式合并,除了每个连通块维护一个平衡树再对全局维护一个平衡树,每个节点表示某一个连通块的最大值.我的常数比较大,危险地卡过去了. #include<cstdio> # ...

  8. bzoj2333: [SCOI2011]棘手的操作 线段树+离线

    网上都是可并堆在线搞,其实直接离线处理处每个联通块,然后把他们放一起,然后点更新,区间询问就可以了. #include <iostream> #include <algorithm& ...

  9. 「SCOI2011」棘手的操作

    传送门 Description 有\(N\)个节点,标号从\(1\)到\(N\),这\(N\)个节点一开始相互不连通.第$ i\(个节点的初始权值为\)a_i$ ,接下来有如下一些操作: U x y ...

最新文章

  1. Linux基础教程之linux文件权限深度解读
  2. QTP的那些事--通过WMI获取session中的用户信息的验证码
  3. android修改系统设置权限_安卓保护隐私利器!完美解决软件「不给权限就不运行」的小工具...
  4. 用C语言实现分治方法数组的排序,C语言实现分治法实例
  5. An error was encountered while running(Domain=LaunchSerivcesError, Code=0)
  6. mysql update返回_MySQL中,当update修改数据与原数据相同时会再次执行吗?
  7. 微型计算机中最小的单位,微型计算机中最小的数据单位是
  8. [python] 1.解释器
  9. 系统检测到您正在使用网页抓取工具访问_网站如何检测被爬虫?
  10. 知识总结2:Django常见面试题总结(持续更新)
  11. 如何使用条形码字体在Crystal Report中创建代码39?
  12. JavaScript基础语法
  13. Python和FFmpeg将语音记录转换成可共享的视频,非常炫酷。
  14. A Bootiful Podcast: Building China-scale Infrastructure at Alibaba with Spring Cloud, Rsocket, and m
  15. ai智能写作软件哪个好-ai智能写作免费
  16. word调整标题编号
  17. 微机原理与接口技术 | 一、微型计算机系统
  18. 自定义控件---继承ViewGroup类方式(循序渐进之第1步效果----图片左右拖动)
  19. 医院信息系统时要注意事项
  20. java 蓝牙_通过Java代码连接到蓝牙设备

热门文章

  1. Async Mode: Future使用
  2. 手把手教你自己写一个js表单验证框架
  3. 推荐系统中常用算法 以及优点缺点对比
  4. git/github的使用
  5. 别把机器学习和人工智能搞混了!
  6. appium 中部分 api 的使用方法
  7. 《Python数据科学指南》——1.16 使用lambda创造匿名函数
  8. 图解字符串的朴素模式匹配算法
  9. FilterDispatcher is deprecated! Please use
  10. Linux中强制结束一个进程的终极方法