BZOJ 2333: [SCOI2011]棘手的操作 可并堆 左偏树 set
https://www.lydsy.com/JudgeOnline/problem.php?id=2333
需要两个结构分别维护每个连通块的最大值和所有连通块最大值中的最大值,可以用两个可并堆实现,也可以用一个可并堆一个平衡树实现,我看的题解用了内置红黑树的set,更加方便,所以我也用了set。
set的用法:https://blog.csdn.net/yas12345678/article/details/52601454 需要注意的是set的find和erase之类的操作返回输入的都是指针,在最后找最大值时,返回的是,也是指针实现的。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
const int maxn=;
multiset<int>s;
int n,q;
int fa[maxn]={},rt[maxn]={},ch[maxn][]={},cnt[maxn]={},add[maxn]={},val[maxn]={},zong=;
char op[]={};
inline void downdata(int x){
if(ch[x][]){val[ch[x][]]+=add[x];add[ch[x][]]+=add[x];}
if(ch[x][]){val[ch[x][]]+=add[x];add[ch[x][]]+=add[x];}
add[x]=;
}
void updata(int x){//这里的updata是把上面的所有标记传递下来
if(fa[x])updata(fa[x]);
downdata(x);
}
int merge(int x,int y){
if(!y)return x;
if(!x) return y;
downdata(x);downdata(y);
if(val[x]<val[y])swap(x,y);
ch[x][]=merge(ch[x][],y);fa[ch[x][]]=x;
if(ch[x][]<ch[x][])swap(ch[x][],ch[x][]);
cnt[x]=cnt[ch[x][]]+;
return x;
}
int Find(int x){
return fa[x]?Find(fa[x]):x;
}
inline int cle(int x){//把x与他的父亲儿子断绝关系,return堆顶
int t=merge(ch[x][],ch[x][]),y=fa[x];
fa[x]=ch[x][]=ch[x][]=;
if(x==ch[y][])ch[y][]=t;
if(x==ch[y][])ch[y][]=t;
fa[t]=y;
return Find(t);
}
inline void del(int x){
s.erase(s.find(x));
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){scanf("%d",&val[i]);s.insert(val[i]);}
scanf("%d",&q);int x,y,xx,yy;
for(int i=;i<=q;i++){
scanf("%s",op);
if(op[]=='A'){
scanf("%d",&x);
if(op[]==''){
scanf("%d",&y);
updata(x);del(val[Find(x)]);val[x]+=y;
s.insert(val[merge(x,cle(x))]);
}
else if(op[]==''){
scanf("%d",&y);
xx=Find(x);del(val[xx]);add[xx]+=y;val[xx]+=y;
s.insert(val[xx]);
}
else zong+=x;
}
else if(op[]=='F'){
if(op[]==''){
scanf("%d",&x);updata(x);printf("%d\n",val[x]+zong);
}
else if(op[]==''){
scanf("%d",&x);
printf("%d\n",val[Find(x)]+zong);
}
else{
printf("%d\n",*(--s.end())+zong);
}
}else{
scanf("%d%d",&x,&y);
yy=Find(y);xx=Find(x);
if(xx!=yy){
if(merge(xx,yy)==xx)del(val[yy]);
else del(val[xx]);
}
}
}
return ;
}
可并堆+set
这道题的另一个写法是离线+线段树,将所有需要并到一起的点离线处理编号在线段树上放到一起,也很清晰。
这里将点排序的方法大概是桶排序?
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
#define lc x*2
#define rc x*2+1
const int maxn=;
int n,q;
char ch[]={};
int val[maxn]={},rig[maxn]={},b[maxn]={},op[maxn][]={};
int e[maxn][]={},fa[maxn]={},cnt=,sum[maxn]={},tail=;
int a[maxn]={};
int xl[maxn*]={},xr[maxn*]={};
int mx[maxn*]={},ad[maxn*]={};
int id1,id2,v,zong=;
int getfa(int x){
return x==fa[x]?x:getfa(fa[x]);
}
inline void downdata(int x,int l,int r){
if(l!=r){
mx[lc]+=ad[x];mx[rc]+=ad[x];
ad[lc]+=ad[x];ad[rc]+=ad[x];
}
ad[x]=;
}
inline void updata(int x,int l,int r){
if(l!=r)mx[x]=max(mx[lc],mx[rc]);
}
void build(int x,int l,int r){
if(l==r){mx[x]=a[l];return;}
downdata(x,l,r);
int mid=(l+r)/;
build(lc,l,mid);
build(rc,mid+,r);
updata(x,l,r);
}
void addit(int x,int l,int r){
if(id1<=l&&r<=id2){ad[x]+=v;mx[x]+=v;return;}
int mid=(l+r)/;
downdata(x,l,r);
if(id1<=mid)addit(lc,l,mid);
if(id2>mid)addit(rc,mid+,r);
updata(x,l,r);
}
int getmx(int x,int l,int r){
if(id1<=l&&r<=id2){return mx[x];}
int mid=(l+r)/,mm=maxn*(-);
downdata(x,l,r);
if(id1<=mid)mm=max(getmx(lc,l,mid),mm);
if(id2>mid)mm=max(getmx(rc,mid+,r),mm);
updata(x,l,r);
return mm;
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){scanf("%d",&val[i]);fa[i]=i;sum[i]=;}
scanf("%d",&q);
int x,y,xx,yy;
for(int i=;i<=q;i++){
scanf("%s",ch);
if(ch[]=='U'){
op[i][]=;
scanf("%d%d",&x,&y);op[i][]=x;op[i][]=y;
xx=getfa(x);yy=getfa(y);
if(xx==yy)continue;
if(xx>yy)swap(xx,yy);
fa[yy]=xx;e[++cnt][]=xx;e[cnt][]=yy;sum[xx]+=sum[yy];
}
else if(ch[]=='A'){
scanf("%d",&op[i][]);
if(ch[]==''){op[i][]=;scanf("%d",&op[i][]);}
else if(ch[]==''){op[i][]=;scanf("%d",&op[i][]);}
else op[i][]=;
}
else{
if(ch[]==''){op[i][]=;scanf("%d",&op[i][]);}
else if(ch[]==''){op[i][]=;scanf("%d",&op[i][]);}
else op[i][]=;
}
}
for(int i=;i<=n;i++){
if(fa[i]==i){
b[i]=tail+;tail+=sum[i];rig[i]=tail;
a[b[i]]=val[i];
}
}
for(int i=cnt;i>;i--){
b[e[i][]]=rig[e[i][]]-sum[e[i][]]+;
rig[e[i][]]=rig[e[i][]];rig[e[i][]]=b[e[i][]]-;
a[b[e[i][]]]=val[e[i][]];
}
for(int i=;i<=n;i++){fa[i]=i;xl[i]=xr[i]=i;/*cout<<i<<b[i]<<endl;*/}
build(,,n);
for(int i=;i<=q;i++){
if(op[i][]==){
xx=getfa(b[op[i][]]);yy=getfa(b[op[i][]]);
if(xx>yy)swap(xx,yy);
xr[xx]=xr[yy];fa[yy]=xx;
}
else if(op[i][]==){id1=b[op[i][]];id2=id1;v=op[i][];addit(,,n);}
else if(op[i][]==){
xx=getfa(b[op[i][]]);
id1=xl[xx];id2=xr[xx];v=op[i][];
addit(,,n);
}
else if(op[i][]==){
zong+=op[i][];
}
else if(op[i][]==){
id1=b[op[i][]];id2=id1;
printf("%d\n",getmx(,,n)+zong);
}
else if(op[i][]==){
xx=getfa(b[op[i][]]);id1=xl[xx];id2=xr[xx];
printf("%d\n",getmx(,,n)+zong);
}
else{
printf("%d\n",mx[]+zong);
}//id1=1;id2=2;
//cout<<getmx(1,1,n)<<' '<<1<<endl;
}
return ;
}
离线+线段树
BZOJ 2333: [SCOI2011]棘手的操作 可并堆 左偏树 set的更多相关文章
- bzoj 2333 [SCOI2011]棘手的操作 —— 可并堆
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2333 稍微复杂,参考了博客:http://hzwer.com/5780.html 用 set ...
- BZOJ 2333 [SCOI2011]棘手的操作 (可并堆)
码农题.. 很显然除了两个全局操作都能用可并堆完成 全局最大值用个multiset记录,每次合并时搞一搞就行了 注意使用multiset删除元素时 如果直接delete一个值,会把和这个值相同的所有元 ...
- BZOJ 2333: [SCOI2011]棘手的操作
题目描述 真的是个很棘手的操作.. 注意每删除一个点,就需要clear一次. #include<complex> #include<cstdio> using namespac ...
- BZOJ 2333 SCOI2011 棘手的操作 并查集+可并堆
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2333 ..题意概述就不写了,各位老爷如果是看着玩的可以去搜一下,如果是做题找来的也知道题干 ...
- 【BZOJ 2333 】[SCOI2011]棘手的操作(离线+线段树|可并堆-左偏树)
2333: [SCOI2011]棘手的操作 Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边 ...
- 2333: [SCOI2011]棘手的操作[写不出来]
2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1979 Solved: 772[Submit][Stat ...
- 2333: [SCOI2011]棘手的操作[离线线段树]
2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2325 Solved: 909[Submit][Stat ...
- 2333: [SCOI2011]棘手的操作[我不玩了]
2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1979 Solved: 772[Submit][Stat ...
- 【bzoj2333】 [SCOI2011]棘手的操作 可并堆+lazy标记
2016-05-31 21:45:41 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2333 (学习了黄学长的代码 有如下操作: U x y ...
随机推荐
- 《区块链100问》第84集:资产代币化之对标黄金Digix
黄金是避险的不二选择.Digix发行的黄金代币则是数字资产世界里的黄金,其代币简称DGX,能够在数字资产世界中起到避险的作用. DGX如何实现对标黄金呢?它将黄金资产进行了上链(即:区块链)操作.举个 ...
- 图片压缩之 PNG
作者:程志达链接:https://zhuanlan.zhihu.com/p/19570424来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. PNG(Portable N ...
- 2016.6.24——vector<vector<int>>【Binary Tree Level Order Traversal】
Binary Tree Level Order Traversal 本题收获: 1.vector<vector<int>>的用法 vector<vector<int ...
- 【Git】Git与GitHub 入门【转】
转自:http://www.cnblogs.com/lcw/p/3394545.html GitHub GitHub是一个基于git的代码托管平台,付费用户可以建私人仓库,我们一般的免费用户只能使用公 ...
- MySQL分布式集群之MyCAT(三)rule的分析【转】
首先写在最前面,MyCAT1.4的alpha版本已经发布了,这里面修复了不少的bug,也完善了一细节,之前两篇博客已经做了一些修改 ---------------------------------- ...
- K/V式枚举
public enum OType { LOGIN { public String getDesc() { return "登录"; } }, ADD { public Strin ...
- 23 The Laws of Reflection 反射定律:反射包的基本原理
The Laws of Reflection 反射定律:反射包的基本原理 6 September 2011 Introduction 介绍 Reflection in computing is th ...
- Python开发环境(3):使用Eclipse+PyDev插件创建Django项目
OS:Windows 10家庭中文版,Python:3.6,Eclipse:Oxygen.1a Release (4.7.1a), PyDev:6.3.2,Django:2.0.3 本文展示了怎么使用 ...
- 【Netty官方文档翻译】引用计数对象(reference counted objects)
知乎有关于引用计数和垃圾回收GC两种方式的详细讲解 https://www.zhihu.com/question/21539353 原文出处:http://netty.io/wiki/referenc ...
- C++之可调用对象
C++中的可调用对象分为以下几种: 函数 函数指针 lambda表达式 bind创建的对象 重载了函数调用运算符(即“()”)的类 函数.函数指针不再介绍.lambda表达式与bind创建的类参考—— ...