您的位置:首页 > 其它

【BZOJ 1180】OTOCI【LCT】&【树链剖分+并查集】

2017-05-07 16:22 393 查看

Description

给出n个结点以及每个点初始时对应的权值wi。起始时点与点之间没有连边。有3类操作:

1、bridge A B:询问结点A与结点B是否连通。如果是则输出“no”。否则输出“yes”,并且在结点A和结点B之间连一条无向边。

2、penguins A X:将结点A对应的权值wA修改为X。

3、excursion A B:如果结点A和结点B不连通,则输出“impossible”。否则输出结点A到结点B的路径上的点对应的权值的和。

给出q个操作,要求在线处理所有操作。数据范围:1<=n<=30000, 1<=q<=300000, 0<=wi<=1000。

Input

第一行包含一个整数n(1<=n<=30000),表示节点的数目。第二行包含n个整数,第i个整数表示第i个节点初始时对应的权值。第三行包含一个整数q(1<=n<=300000),表示操作的数目。以下q行,每行包含一个操作,操作的类别见题目描述。任意时刻每个节点对应的权值都是1到1000的整数。

Output

输出所有bridge操作和excursion操作对应的输出,每个一行。

题解

一、LCT

本题最基本的方法

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
#define N 60010
#define lson node[o].ls
#define rson node[o].rs

struct LCT{
struct Node{
int idx,fa,ls,rs,path_fa;
} node
;
int cnt,pos
,a
,tot
;
bool ch
;

inline void maintain(int o)
{
if(ch[o]) {
ch[o] = false;
ch[lson] = !ch[lson]; ch[rson] = !ch[rson];
swap(lson,rson);
}
tot[o] = tot[lson] + tot[rson] + a[node[o].idx];
}

void l_rotate(int o)
{
int y = node[o].fa;
node[o].path_fa = node[y].path_fa;
node[y].path_fa = 0;
node[y].rs = lson;
int tmp = tot[y];
tot[y] = tot[y] - tot[o] + tot[lson];
tot[o] = tmp;
if(lson) node[lson].fa = y;
node[o].fa = node[y].fa;
if(node[y].fa) {
if(y == node[node[y].fa].ls) node[node[y].fa].ls = o;
else node[node[y].fa].rs = o;
}
node[y].fa = o;
node[o].ls = y;
}

void r_rotate(int o)
{
int y = node[o].fa;
node[o].path_fa = node[y].path_fa;
node[y].path_fa = 0;
node[y].ls = rson;
int tmp = tot[y];
tot[y] = tot[y] - tot[o] + tot[rson];
tot[o] = tmp;
if(rson) node[rson].fa = y;
node[o].fa = node[y].fa;
if(node[y].fa) {
if(y == node[node[y].fa].ls) node[node[y].fa].ls = o;
else node[node[y].fa].rs = o;
}
node[y].fa = o;
node[o].rs = y;
}

void splay(int o)
{
while(node[o].fa) {
int pa = node[o].fa;
maintain(pa);
if(node[pa].ls) maintain(node[pa].ls);
if(node[pa].rs) maintain(node[pa].rs);
if(o == node[node[o].fa].ls) r_rotate(o); else l_rotate(o);
}
}

void access(int o)
{
splay(o);
maintain(o);
int q = rson;
rson = node[q].fa = 0;
node[q].path_fa = o;
maintain(o);
for(q = node[o].path_fa;q;q = node[o].path_fa) {
splay(q);
maintain(q);
int r = node[q].rs;
node[r].fa = 0;
node[r].path_fa = q;
node[q].rs = o;
node[o].fa = q;
node[o].path_fa = 0;
maintain(q);
o = q;
}
splay(o);
}

int find_root(int o)
{
access(o); splay(o);
maintain(o);
while(lson) o = lson;
splay(o);
return o;
}

void evert(int o)
{
access(o); splay(o);
ch[o] = !ch[o];
maintain(o);
}

void cut(int p,int q)
{
evert(p);
access(q); splay(q);
maintain(q);
node[node[q].ls].fa = 0;
node[q].ls = 0;
maintain(q);
}

void link(int p,int q)
{
evert(p); splay(p); maintain(p);
access(q); splay(q); maintain(q);
node[p].ls = q;
node[q].fa = p;
maintain(p);
}

int sum(int p,int q)
{
evert(p); splay(p); maintain(p);
access(q); splay(q); maintain(q);
return tot[q];
}

void change(int o,int d,int pre)
{
splay(o);
tot[o] += d - pre;
}

void init()
{
cnt = 0;
memset(pos,0,sizeof(pos));
tot[0] = 0;
}

void make_tree(int idx,int d)
{
a[idx] = d;
int o = ++cnt;
pos[idx] = o;
node[o].fa = lson = rson = node[o].path_fa = 0;
tot[o] = d;
node[o].idx = idx;
}

int getroot(int idx) { return node[find_root(idx)].idx; }
void add_edge(int x,int y) { link(pos[x],pos[y]); }
void destory(int x,int y) { cut(pos[x],pos[y]); }
int getsum(int x,int y) { return sum(pos[x],pos[y]); }
void Change(int x,int d) { change(pos[x],d,a[x]); a[x] = d; }
}T;

int n,u,v,q;
char str[20];

int main()
{
scanf("%d",&n);
T.init();
for(int i = 1;i <= n;i++) {
scanf("%d",&v);
T.make_tree(i,v);
}
scanf("%d",&q);
while(q--) {
scanf("%s%d%d",str,&u,&v);
if(str[0] == 'b') {
if(T.getroot(u) == T.getroot(v)) puts("no");
else {puts("yes"); T.add_edge(u,v);}
}
else if(str[0] == 'p') T.Change(u,v);
else {
if(T.getroot(u) != T.getroot(v)) puts("impossible");
else printf("%d\n",T.getsum(u,v));
}
}
return 0;
}


二、树链剖分+并查集

  我个人更喜欢这一种,好写一点。

  考虑到本题没有删边操作,所以一开始将所有的添加边搞出来,确定树的形态(注意,可能是一棵森林,在树链剖分dfs的时候要注意

  然后再用并查集维护连通性即可。

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
#define N 30100
#define M 300100

struct node{int to,next;}e[N<<2];
int head
,tot;

int tid
,son
,fa
,top
,dep
,size
,Rank
;
int n,a
,tim;
bool vis
;

void init()
{
tot = tim = 0;
memset(head,0,sizeof(head));
memset(son,-1,sizeof(son));
memset(vis,false,sizeof(vis));
}

void add_edge(int from,int to)
{
e[++tot].next = head[from];
head[from] = tot;
e[tot].to = to;
}

void dfs1(int v,int pa,int deep)
{
dep[v] = deep; fa[v] = pa; size[v] = 1;
vis[v] = 1;
for(int i = head[v];i;i = e[i].next)
if(e[i].to != pa) {
dfs1(e[i].to,v,deep+1);
size[v] += size[e[i].to];
if(son[v] == -1 || size[e[i].to] > size[son[v]])
son[v] = e[i].to;
}
}

void dfs2(int v,int tp)
{
top[v] = tp; tid[v] = ++tim;
Rank[tid[v]] = v;
if(son[v] == -1) return;
dfs2(son[v],tp);
for(int i = head[v];i;i=e[i].next)
if(e[i].to != son[v] && e[i].to != fa[v])
dfs2(e[i].to,e[i].to);
}

int f
;
int find(int x) { return x == f[x] ? x : f[x] = find(f[x]);}

#define lson o << 1
#define rson o << 1 | 1
int sum[N<<2];

void build(int o,int l,int r)
{
if(l == r) {sum[o] = a[Rank[l]]; return;}
int mid = (l+r)>>1;
build(lson,l,mid); build(rson,mid+1,r);
sum[o] = sum[lson] + sum[rson];
}

void update(int o,int l,int r,int pos,int v)
{
if(l == r) {sum[o] = v; return;}
int mid = (l+r)>>1;
if(pos <= mid) update(lson,l,mid,pos,v); else update(rson,mid+1,r,pos,v);
sum[o] = sum[lson] + sum[rson];
}

int query(int o,int l,int r,int ll,int rr)
{
if(ll <= l && rr >= r) return sum[o];
int mid = (l+r)>>1;
int ans = 0;
if(ll <= mid) ans += query(lson,l,mid,ll,rr);
if(rr > mid) ans += query(rson,mid+1,r,ll,rr);
return ans;
}

int Query(int x,int y)
{
int ans = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x,y);
ans += query(1,1,n,tid[top[x]],tid[x]);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
ans += query(1,1,n,tid[x],tid[y]);
return ans;
}

int A[M],B[M],opt[M];
int m;
char str[20];
int main()
{
init();
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
scanf("%d",&m);

for(int i = 1;i <= n;i++) f[i] = i;
for(int i = 1;i <= m;i++) {
scanf("%s%d%d",str,&A[i],&B[i]);
if(str[0] == 'b') {
opt[i] = 1;
int x = find(A[i]),y = find(B[i]);
if(x != y) {
f[x] = y;
add_edge(A[i],B[i]); add_edge(B[i],A[i]);
}
}
if(str[0] == 'p') opt[i] = 2;
if(str[0] == 'e') opt[i] = 3;
}
for(int i = 1;i <= n;i++)
if(!vis[i]) dfs1(i,0,0),dfs2(i,i);
build(1,1,n);

for(int i = 1;i <= n;i++) f[i] = i;
for(int i = 1;i <= m;i++) {
if(opt[i] == 1) {
int x = find(A[i]),y = find(B[i]);
if(x != y) {puts("yes"); f[x] = y;} else puts("no");
}
if(opt[i] == 2) update(1,1,n,tid[A[i]],B[i]);
if(opt[i] == 3) {
int x = find(A[i]),y = find(B[i]);
if(x != y) puts("impossible"); else printf("%d\n",Query(A[i],B[i]));
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: