题目链接:棘手的操作

  网上的题解大部分都是在线用可并堆艹……但是树高严格\(\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 2147483647 using 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 300010 using 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;
}

BZOJ 2333 【SCOI2011】 棘手的操作的更多相关文章

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

    题目描述 真的是个很棘手的操作.. 注意每删除一个点,就需要clear一次. #include<complex> #include<cstdio> using namespac ...

  2. BZOJ 2333: [SCOI2011]棘手的操作 可并堆 左偏树 set

    https://www.lydsy.com/JudgeOnline/problem.php?id=2333 需要两个结构分别维护每个连通块的最大值和所有连通块最大值中的最大值,可以用两个可并堆实现,也 ...

  3. BZOJ 2333 SCOI2011 棘手的操作 并查集+可并堆

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2333 ..题意概述就不写了,各位老爷如果是看着玩的可以去搜一下,如果是做题找来的也知道题干 ...

  4. bzoj 2333 [SCOI2011]棘手的操作 —— 可并堆

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2333 稍微复杂,参考了博客:http://hzwer.com/5780.html 用 set ...

  5. BZOJ 2333 [SCOI2011]棘手的操作 (可并堆)

    码农题.. 很显然除了两个全局操作都能用可并堆完成 全局最大值用个multiset记录,每次合并时搞一搞就行了 注意使用multiset删除元素时 如果直接delete一个值,会把和这个值相同的所有元 ...

  6. 2333: [SCOI2011]棘手的操作[写不出来]

    2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1979  Solved: 772[Submit][Stat ...

  7. 2333: [SCOI2011]棘手的操作[离线线段树]

    2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2325  Solved: 909[Submit][Stat ...

  8. 2333: [SCOI2011]棘手的操作[我不玩了]

    2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1979  Solved: 772[Submit][Stat ...

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

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

  10. 【BZOJ 2333 】[SCOI2011]棘手的操作(离线+线段树)

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

随机推荐

  1. Egret P2 ( 一) 掉落的小球

    实际效果(蛋疼这个gif制作软件,帧率太低....): 一 Egret和P2的坐标系 首先了解下P2和Egret的坐标系. 某人链接:http://blog.csdn.net/qilei2010/ar ...

  2. 【BZOJ3362-3365】USACO水题四连A

    [BZOJ3362][Usaco2004 Feb]Navigation Nightmare 导航噩梦 Description     农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤ ...

  3. ios 在UIView上画图,线条

    1.画线条(实线,虚线) - (void)drawRect:(CGRect)rect {     CGContextRef context = UIGraphicsGetCurrentContext( ...

  4. 日志记录---log4j详解

    Apache官方项目地址 通常的日志记录的缺点是会减慢程序的运行速度,如果用普通的System.out的话影响视觉效果,另外解耦度也不好,而log4j的设计则使得日志记录变得可靠快速和可拓展性好. l ...

  5. ThinkPHP分类查询(获取当前分类的子分类,获取父分类,下一级分类)

    获取指定分类的所有子分类ID号 //获取指定分类的所有子分类ID号 function getAllChildcateIds($categoryID){ //初始化ID数组 $array[] = $ca ...

  6. 最小圆覆盖(随机增量法&模拟退火法)

    http://acm.hdu.edu.cn/showproblem.php?pid=3007 相关题型连接: http://acm.hdu.edu.cn/showproblem.php?pid=393 ...

  7. 二叉树各种相关操作(建立二叉树、前序、中序、后序、求二叉树的深度、查找二叉树节点,层次遍历二叉树等)(C语言版)

    将二叉树相关的操作集中在一个实例里,有助于理解有关二叉树的相关操作: 1.定义树的结构体: typedef struct TreeNode{ int data; struct TreeNode *le ...

  8. vue.js实现初了解(一)

    1. vue 2.0是用Flow做静态类型检查, 3.0对TypeScript的支持更好了: 2. vue.js是基于Rollup(更轻量,适合js库的构建)构建的,它的构建相关配置都在scripts ...

  9. Xshell配置ssh免密码登录-密钥公钥(Public key)

    1 简介 ssh登录提供两种认证方式:口令(密码)认证方式和密钥认证方式.其中口令(密码)认证方式是我们最常用的一种,这里介绍密钥认证方式登录到linux/unix的方法. 使用密钥登录分为3步: 1 ...

  10. 170731、Nginx初探

    一. 概念 Nginx——Ngine X,是一款自由的.开源的.高性能HTTP服务器和反向代理服务器:也是一个IMAP.POP3.SMTP代理服务器:也就是说Nginx本身就可以托管网站(类似于Tom ...