1. 程式人生 > >【BZOJ 1180】OTOCI【LCT】&【樹鏈剖分+並查集】

【BZOJ 1180】OTOCI【LCT】&【樹鏈剖分+並查集】

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[N]; int cnt,pos[N],a[N],tot[N]; bool ch[N]; 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[N],tot;

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

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[N];
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;
}

相關推薦

BZOJ 1180OTOCILCT&+

Description 給出n個結點以及每個點初始時對應的權值wi。起始時點與點之間沒有連邊。有3類操作: 1、bridge A B:詢問結點A與結點B是否連通。如果是則輸出“no”。否則輸出“yes”,並且在結點A和結點B之間連一條無向邊。 2、pe

BZOJ1969[Ahoi2005]LANE 航線規劃 離線++線段

個數 wap 鎖定 樹邊 mes zoj swap 相同 swa 【BZOJ1969】[Ahoi2005]LANE 航線規劃 Description 對Samuel星球的探險已經取得了非常巨大的成就,於是科學家們將目光投向了Samuel星球所在的星系—&md

BZOJ4811[Ynoi2017]由乃的OJ +線段

size 數值 sin 分數 strong str blank cstring else 【BZOJ4811】[Ynoi2017]由乃的OJ Description 由乃正在做她的OJ。現在她在處理OJ上的用戶排名問題。OJ上註冊了n個用戶,編號為1~",一開始他們

bzoj [Usaco2010 Hol]cowpol 奶牛政壇

clu int == fat void += char tdi ios 意識流虛樹 首先考慮只有一個黨派,那麽可以O(n)求樹的直徑,步驟是隨便指定一個根然後找距離根最遠點,然後再找距離這個最遠點最遠的點,那麽最遠點和距離這個最遠點最遠的點之間的距離就是直徑 那麽考慮多黨派

bzoj [JSOI2010]Group 部落劃分 Group二分+

group print i++ end cst col sqrt 部落 har 我是zz嗎這麽簡單都寫錯…… 一眼二分,然後判斷的話是枚舉點,然後計算這個點到已有聯通塊的最小距離,如果這個點到一些聯通塊的距離小於當前二分的val,則把這些聯通塊合並起來,這裏用並查集維護,最

bzoj 1015: [JSOI2008]星球大戰starwar

const \n ans 聯通 ons zha stream for urn 又犯了zz的錯誤…… 需要註意的是,被毀掉的星球是不算一個聯通塊的(可能只有我這麽算吧= =) 離線下來時間倒流,就變成了向圖裏加星球,也就是用並查集維護聯通,在用tot變量記錄當前答案,每加一個

bzoj 2157: 旅遊+線段

ios else add n) -- lse names for swa 裸的樹鏈剖分+線段樹 但是要註意一個地方……我WA了好幾次才發現取完相反數之後max值和min值是要交換的…… #include<iostream> #include<cstdio&

bzoj 1787: [Ahoi2008]Meet 緊急集合lca

== ace dfs tdi 但是 stream -- max i++ 對於三個點求最小路徑長度和,答案肯定在某兩個點的lca上,因為如果把集合點定在公共lca上,一定有兩個點匯合後再一起上到lca,這樣顯然不如讓剩下的那個點下來 這個lca可能是深度最深的……但是我懶得證

LCT+BZOJ2959[長跑]題解

題目概述 CHNJZ可以在 n 個地方虐場,每次虐場可以踩若干個人。一個地方的人被踩後就不能再踩了(心態已爆炸)。 有 m 個事件:1.地點 x 到地點 y 新建了一條邊。2.地點 x 能踩的人變成了 y 。3.詢問從 x 到 y 最多能踩多少人。 解

BZOJ 1854: [Scoi2010]遊戲 二分圖

Description lxhgww最近迷上了一款遊戲,在遊戲裡,他擁有很多的裝備,每種裝備都有2個屬性,這些屬性的值用[1,10000]之間的數表示。當他使用某種裝備時,他只能使用該裝備的某一個屬性。並且每種裝備最多隻能使用一次。 遊戲進行到最後,lxhgw

NOI 2015bzoj 4195程式自動分析

4195: [Noi2015]程式自動分析 Time Limit: 10 Sec Memory Limit: 512 MB Submit: 294 Solved: 149 Description 在實現程式自動分析的過程中,常常需要判定一些約束條件是

bzoj 4199: [Noi2015]品酒大會後綴數組+單調棧+

++ cer bool code getc ons %s height swap 用SA求出height數組,然後發現每個height值都有一個貢獻區間(因為點對之間要依次取min) 用單調棧處理出區間,第一問就做完了 然後用並查集維護每個點的貢獻(?),從大到小枚舉hei

hdoj-1856-More is better

sub ont max ash cer careful gin search std More is better Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 327680/102400 K (Java/Ot

bzoj2836魔法 +線段

urn fin pan online char font -s class efi 題目描述 輸入 輸出 樣例輸入 4 0 1 1 2 2 3 4 Add 1 3 1 Query 0 Query 1 Query 2 樣例輸出

LSGDOJ1834 Tree

done using 給定 continue 返回 ace 最大的 接下來 chan 題目描述 給定一個N個結點的無向樹,樹中的結點按照1...N編號,樹中的邊按照1...N ? 1編號,每條邊都賦予一個權值。你需要編寫程序支持以下三種操作: 1. CHANGE i

HDU1863_暢通projectPrim

計數 道路 不足 rim scanf article ava 能夠 else 暢通project Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Ja

洛谷——P3384 模板

upload mes 事情 -- aliyun pro 格式 路徑 sca https://www.luogu.org/problem/show?pid=3384#sub 題目描述 如題,已知一棵包含N個結點的樹(連通且無環),每個節點上包含一個數值,需要支持以下操作:

CodeForces 776D The Door Problem

merge cnblogs 表示 turn pro name 所有 force mes CodeForces 776D The Door Problem【並查集】並查集 設 f 1--m 表示 開的情況 m+1--2*m 表示關的情況 對於每盞燈 如果他 是關

裸的POJ 1611 The Suspects

space lose %d cst one accep poj find can http://poj.org/problem?id=1611 【Accepted】 1 #include<iostream> 2 #include<cstdio>

BZOJ2843極地旅行社 離線++狀數組

i++ data 代碼 == 範圍 cst string input 樹狀 【BZOJ2843】極地旅行社 Description 不久之前,Mirko建立了一個旅行社,名叫“極地之夢”。這家旅行社在北極附近購買了N座冰島,並且提供觀光服務。