您的位置:首页 > 其它

[BZOJ2333][SCOI2011]棘手的操作(可并堆||线段树+离线)

2017-01-07 15:18 417 查看

题目描述

传送门

题解

对没错总会有一道题分到这个编号…233333

第一眼想到线段树,处理生成树(重新编号)使在任何时间在同一个连通块里的点都在一个连续的区间,然后搞搞搞就可以了

然而要是换成可并堆的话操作还真是棘手啊…

首先我们要维护两种可并堆(一个可并堆和一个splay也可以…),以下简称a堆和b堆

a堆是按照题目中的操作合并的,也就是说有若干个小堆

b堆只有一个堆,维护的是所有a堆的堆顶(最大值),也就是说堆中的节点数等于a堆的个数

a中每一个堆顶要维护一个标记add,表示当前这个堆中键值+add为实际值

维护全局ADD,表示总体加的值

U x y :在a中找到x和y中的根fx和fy,在b中删除fx和fy;在a中合并x和y,得到新的根top;将top插入b

A1 x v :在a中找到x的根fx,在a中删除x,在b中删除fx;修改x的键值,将x插入a,得到新根top;将top插入b

A2 x v :在a中找到x的根fx,在b中删除fx;在a中修改fx的标记,在b中修改fx的键值;在b中插入fx

A3 x :ADD+=v

F1 x :在a中找到x的根fx,key(x)+add(fx)+ADD

F2 x :在a中找到x的根fx,key(fx)+add(fx)+ADD

F3 x :找到b的根fx,key(fx)+ADD

注意:

a中每个根有标记add,堆中的键值+堆顶add才是正确值;b中没有标记,维护的键值即为实际值

U中将a堆合并时由于堆顶标记不同,需要修改某一个堆中的键值,使两堆标记相同。用启发式合并,每次暴力修改较小的堆,每个点只会被合并至多logn次

时间复杂度O(nlogn)*一坨常数

写完可并堆了之后发现线段树的写法之前从来没写过又去写了一发…

值得一做的题.

代码

可并堆

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define N 300005

int n,m,x,y,fx,fy,top,tmp,temp,v,ADD,lastsize,lastadd;
int ls
,rs
,dis
,key
,f
,add
,par
,size
;
char opt[5];
queue <int> q;

void Add(int x,int v)//启发式
{
if (ls[x]) Add(ls[x],v);
if (rs[x]) Add(rs[x],v);
key[x]+=v;
return;
}
int find(int x)//找根
{
if (x==f[x]) return x;
f[x]=find(f[x]);
return f[x];
}
int merge(int x,int y)//合并
{
if (!x) return y;
if (!y) return x;
if (key[x]<key[y]) swap(x,y);
rs[x]=merge(rs[x],y);
par[rs[x]]=x;
if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
if (!rs[x]) dis[x]=0;
else dis[x]=dis[rs[x]]+1;
return x;
}
int del(int x)//删除x,返回x原先所在堆中的根(若删除x之后堆为空,则返回0)
{
int p,q,re;
q=par[x];
p=merge(ls[x],rs[x]);
if (p) par[p]=q;
if (q&&ls[q]==x) ls[q]=p;
if (q&&rs[q]==x) rs[q]=p;
if (!q) re=p;
else re=find(q);
while (q)
{
if (dis[ls[q]]<dis[rs[q]])
swap(ls[q],rs[q]);
if (dis[rs[q]]+1==dis[q])
break;
dis[q]=dis[rs[q]]+1;
q=par[q];
}
ls[x]=rs[x]=dis[x]=par[x]=0;
return re;
}

namespace st
{
int ls
,rs
,dis
,key
,par
;
int root;
int merge(int x,int y)//合并
{
if (!x) return y;
if (!y) return x;
if (key[x]<key[y]) swap(x,y);
rs[x]=merge(rs[x],y);
par[rs[x]]=x;
if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
if (!rs[x]) dis[x]=0;
else dis[x]=dis[rs[x]]+1;
return x;
}
void clear()//左偏树构建
{
for (int i=1;i<=n;++i) q.push(i);
while (!q.empty())
{
x=q.front();q.pop();
if (!q.empty()) {y=q.front();q.pop();}
else break;
top=merge(x,y);
q.push(top);
}
st::root=x;
}
int del(int x)//删除x,返回根(可能为0)
{
int p,q;
q=par[x];
p=merge(ls[x],rs[x]);
if (p) par[p]=q;
if (q&&ls[q]==x) ls[q]=p;
if (q&&rs[q]==x) rs[q]=p;
if (!q) root=p;
while (q)
{
if (dis[ls[q]]<dis[rs[q]])
swap(ls[q],rs[q]);
if (dis[q]==dis[rs[q]]+1)
break;
dis[q]=dis[rs[q]]+1;
p=q;
q=par[q];
}
ls[x]=rs[x]=dis[x]=par[x]=0;
f[x]=x;
return root;
}
}

int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
{
scanf("%d",&key[i]);
st::key[i]=key[i];
}
for (int i=1;i<=n;++i) f[i]=i,size[i]=1;
dis[0]=st::dis[0]=-1;
st::clear();
scanf("%d",&m);
for (int i=1;i<=m;++i)
{
scanf("%s",opt);
if (opt[0]=='U')
{
scanf("%d%d",&x,&y);
fx=find(x),fy=find(y);
if (fx!=fy)
{
tmp=st::del(fx);
tmp=st::del(fy);

if (size[fx]>size[fy])
swap(x,y),swap(fx,fy);
Add(fx,add[fx]-add[fy]);
top=merge(fx,fy);
if (fx) f[fx]=top;
if (fy) f[fy]=top;
size[top]=size[fx]+size[fy];
add[top]=add[fy];

temp=st::merge(tmp,top);
st::root=temp;
}
}
else if (opt[0]=='A')
{
if (opt[1]=='1')
{
scanf("%d%d",&x,&v);
fx=find(x);

tmp=st::del(fx);

lastsize=size[fx];
lastadd=add[fx];
y=del(x);
key[x]+=v;
top=merge(x,y);
if (x) f[x]=top;
if (y) f[y]=top;
size[top]=lastsize;
add[top]=lastadd;

st::key[top]=key[top]+add[top];
temp=st::merge(tmp,top);
st::root=temp;
}
else if (opt[1]=='2')
{
scanf("%d%d",&x,&v);
fx=find(x);

tmp=st::del(fx);

add[fx]+=v;

st::key[fx]=key[fx]+add[fx];
temp=st::merge(tmp,fx);
st::root=temp;
}
else if (opt[1]=='3')
{
scanf("%d",&v);
ADD+=v;
}
}
else if (opt[0]=='F')
{
if (opt[1]=='1')
{
scanf("%d",&x);
fx=find(x);
printf("%d\n",key[x]+add[fx]+ADD);
}
else if (opt[1]=='2')
{
scanf("%d",&x);
fx=find(x);
printf("%d\n",key[fx]+add[fx]+ADD);
}
else if (opt[1]=='3')
{
x=st::root;
printf("%d\n",st::key[x]+ADD);
}
}
}
return 0;
}


线段树+离线

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define N 300005
#define inf 2100000000

int n,m,fx,fy,cnt,ans;
char opt
[3];
int a
,x
,y
,v
,f
,size
,from
,to
,l
,r
,pt
,val
;
int maxn[N*4],delta[N*4];

int find(int x)
{
if (x==f[x]) return x;
f[x]=find(f[x]);
return f[x];
}
void update(int now)
{
maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
}
void pushdown(int now,int l,int r,int mid)
{
if (delta[now])
{
maxn[now<<1]+=delta[now];delta[now<<1]+=delta[now];
maxn[now<<1|1]+=delta[now];delta[now<<1|1]+=delta[now];
delta[now]=0;
}
}
void build(int now,int l,int r)
{
int mid=(l+r)>>1;
if (l==r)
{
maxn[now]=val[l];
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void change(int now,int l,int r,int lrange,int rrange,int v)
{
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange)
{
maxn[now]+=v;
delta[now]+=v;
return;
}
pushdown(now,l,r,mid);
if (lrange<=mid) change(now<<1,l,mid,lrange,rrange,v);
if (mid+1<=rrange) change(now<<1|1,mid+1,r,lrange,rrange,v);
update(now);
}
int query(int now,int l,int r,int lrange,int rrange)
{
int mid=(l+r)>>1,ans=-inf;
if (lrange<=l&&r<=rrange) return maxn[now];
pushdown(now,l,r,mid);
if (lrange<=mid) ans=max(ans,query(now<<1,l,mid,lrange,rrange));
if (mid+1<=rrange) ans=max(ans,query(now<<1|1,mid+1,r,lrange,rrange));
return ans;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
for (int i=1;i<=n;++i) f[i]=i,size[i]=1;
scanf("%d",&m);
for (int i=1;i<=m;++i)
{
scanf("%s",opt[i]);
if (opt[i][0]=='U')
{
scanf("%d%d",&x[i],&y[i]);
fx=find(x[i]),fy=find(y[i]);
if (fx==fy) continue;
if (fx<fy) swap(x[i],y[i]),swap(fx,fy);
size[fy]+=size[fx];
f[fx]=fy;
from[++cnt]=fx;to[cnt]=fy;
}
else if (opt[i][0]=='A')
{
if (opt[i][1]=='1')
scanf("%d%d",&x[i],&v[i]);
else if (opt[i][1]=='2')
scanf("%d%d",&x[i],&v[i]);
else if (opt[i][1]=='3')
scanf("%d",&v[i]);
}
else if (opt[i][0]=='F')
{
if (opt[i][1]=='1')
scanf("%d",&x[i]);
else if (opt[i][1]=='2')
scanf("%d",&x[i]);
}
}
int last=0;
for (int i=1;i<=n;++i)
if (find(i)==i&&size[i]>1)
{
l[i]=last+1;
r[i]=pt[i]=last+size[i];
last+=size[i];
}
for (int i=cnt;i>=1;--i)
{
r[from[i]]=pt[from[i]]=pt[to[i]];
l[from[i]]=r[from[i]]-size[from[i]]+1;
pt[to[i]]=l[from[i]]-1;
}
for (int i=1;i<=n;++i)
if (find(i)==i&&size[i]==1)
l[i]=r[i]=++last;
for (int i=1;i<=n;++i) f[i]=i,size[i]=1,val[l[i]]=a[i];
build(1,1,n);
for (int i=1;i<=m;++i)
{
if (opt[i][0]=='U')
{
fx=find(x[i]),fy=find(y[i]);
if (fx==fy) continue;
if (fx<fy) swap(fx,fy),swap(x[i],y[i]);
f[fx]=fy;
size[fy]+=size[fx];
}
else if (opt[i][0]=='A')
{
if (opt[i][1]=='1')
change(1,1,n,l[x[i]],l[x[i]],v[i]);
else if (opt[i][1]=='2')
{
fx=find(x[i]);
change(1,1,n,l[fx],l[fx]+size[fx]-1,v[i]);
}
else if (opt[i][1]=='3')
change(1,1,n,1,n,v[i]);
}
else if (opt[i][0]=='F')
{
if (opt[i][1]=='1')
{
ans=query(1,1,n,l[x[i]],l[x[i]]);
printf("%d\n",ans);
}
else if (opt[i][1]=='2')
{
fx=find(x[i]);
ans=query(1,1,n,l[fx],l[fx]+size[fx]-1);
printf("%d\n",ans);
}
else if (opt[i][1]=='3')
{
ans=query(1,1,n,1,n);
printf("%d\n",ans);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: