1. 程式人生 > >[洛谷P3401] 洛谷樹

[洛谷P3401] 洛谷樹

百度 pre names update 什麽 其中 說明 修改 在線

洛谷題目連接:洛谷樹

題目背景

萌噠的Created equal小倉鼠種了一棵洛谷樹!

(題目背景是辣雞小倉鼠亂寫的QAQ)。

題目描述

樹是一個無環、聯通的無向圖,由n個點和n-1條邊構成。樹上兩個點之間的路徑被定義為他們之間的唯一一條簡單路徑——顯然這是一條最短路徑。

現在引入一個概念——子路徑。假設樹上兩個點p1和pn之間的路徑是P=<p1,p2,p3,…,pn>,那麽它的子路徑被定義為某一條路徑P’,滿足P’=<pi,pi+1,pi+2,…,pj>,其中1<=i<=j<=n。顯然,原路徑是一條子路徑,任意一個點也可以作為子路徑。

我們給每條邊賦予一個邊權。萌萌噠的Sugar問小倉鼠:對於任意兩個點u和v,你能快速求出,u到v的路徑上所有子路徑經過的邊的邊權的xor值的和是多少。具體地說就是,你把u到v的路徑上所有子路徑全部提出來,然後分別把每個子路徑上經過的邊的邊權xor在一起,最後求出得到的所有xor值的和。

什麽?你不知道xor?那就去百度啊!

這時候,fjzzq2002大爺冒了粗來:窩還要你滋磁修改某條邊邊權的操作!

小倉鼠那麽辣雞,當然不會做這道題啦。於是他就來向你求救!

輸入輸出格式

輸入格式:

第一行兩個正整數n和q,表示點的個數,查詢和詢問的總次數。

接下來n-1行,每行兩個正整數u、v、w,表示u和v兩個點之間有一條邊權為w的邊。

接下來q行,格式為1 u v或2 u v w。如果為1 u v操作,你需要輸出u到v的路徑上所有子路徑經過的邊的邊權的xor值的和是多少;如果為2 u v w操作,你需要把u到v這條邊的邊權改為w,保證這條邊存在。

輸出格式:

對於每個1操作,輸出答案。

輸入輸出樣例

輸入樣例#1:

5 3
1 2 3
2 3 3
2 4 6
4 5 1
1 3 4
2 2 4 7
1 3 5

輸出樣例#1:

14
26

說明

本題時限1s,內存限制128M,因新評測機速度較為接近NOIP評測機速度,請註意常數問題帶來的影響。

【數據範圍】

No    n=    q=    備註
1    100    5    無
2    100    20    無
3    100    100    無
4    5000    1000    無
5    5000    2000    無
6    5000    3000    無
7    10000    10000    第i條邊連接第i個點和第i+1個點,且沒有2操作
8    10000    20000    第i條邊連接第i個點和第i+1個點,且沒有2操作
9    10000    10000    第i條邊連接第i個點和第i+1個點
10    10000    20000    第i條邊連接第i個點和第i+1個點
11    10000    10000    沒有2操作
12    10000    20000    沒有2操作
13    20000    20000    沒有2操作
14    30000    30000    沒有2操作
15    30000    10000    無
16    20000    20000    無
17    20000    20000    無
18    30000    20000    無
19    20000    30000    無
20    30000    30000    無

對於100%的數據,所有邊權小於等於1023。

一句話題意: 給出一棵樹,要求出一條路徑上的所有子段的異或值的和.並且要求支持修改操作.

題解: 要求一條路徑上的子段的異或值的和,如果直接用樸素算法的話,需要枚舉這個子段的兩個端點.那麽這樣復雜度就是\(O(n^2)\)的了.

我們先來考慮這樣一種情況:假設邊的權值只有0和1.

因為題目要求的是把所有子段的異或和加起來,所以先將每個點到根的異或和統計起來,用樹剖維護到線段樹中.因為0與0異或後仍然是0,1與1異或也是,而這樣都不能對答案做出貢獻,所以就不用考慮加入答案.只有一個0和一個1異或後的值為1,也就是說答案就是0與1可以形成的組合的數量,也就是從根節點開始的異或和為0的個數乘以1的個數.

那麽對於那些權值不是0和1的也可以轉化成二進制的形式,然後再按位處理,也就是說,對於一條路徑的查詢可以分成11次查詢的和(1023\(\leq2^{10}\)),分別查詢第0~第10位的答案之和.

然後對於修改操作,因為我們在線段樹中維護的是異或和,所以需要將修改的那條邊的子樹中的值都要修改(對於二進制每一位的修改),因為只有0和1,所以直接將區間的信息翻轉一下就可以了.

可能講的有點不清楚,可以通過樣例模擬一下,然後看下代碼理解一下吧.

#include<bits/stdc++.h>
#define ll(x) (x<<1)
#define rr(x) (x<<1|1)
using namespace std;
typedef int _int;
#define int long long
const int N=30000+5;

int n, m, ecnt = 0, last[N], val[N], pre[N];
int size[N], son[N], fa[N], top[N], id[N], idx = 0, dep[N], vis[N];

struct edge{
    int to, nex, w;
}e[N*2];

struct segment_tree{
    int l, r, size, res1, tag;
    segment_tree operator + (const segment_tree a) const{
        segment_tree temp;
        temp.size = size+a.size;
        temp.res1 = res1+a.res1;
        return temp;
    }
}t[N*4][12];

int gi(){
    int ans = 0, f = 1; char i = getchar();
    while(i<'0' || i>'9'){ if(i == '-') f = -1; i = getchar(); }
    while(i>='0' && i<='9') ans = ans*10+i-'0', i = getchar();
    return ans * f;
}

void add(int x, int y, int z){
    e[++ecnt].to = y;
    e[ecnt].w = z;
    e[ecnt].nex = last[x];
    last[x] = ecnt;
}

void up(int x, int k){ t[x][k].res1 = t[ll(x)][k].res1+t[rr(x)][k].res1; }

void dfs1(int x, int f, int deep){
    size[x] = 1, dep[x] = deep, fa[x] = f;
    for(int to, i=last[x];i;i=e[i].nex){
        to = e[i].to;
        if(to == f) continue;
        pre[to] = e[i].w^pre[x], val[to] = e[i].w;
        dfs1(to, x, deep+1); size[x] += size[to];
        if(size[to] > size[son[x]]) son[x] = to;
    }
}

void dfs2(int x, int f, int tp){
    top[x] = tp, id[x] = ++idx, vis[idx] = x;
    if(son[x]) dfs2(son[x], x, tp);
    for(int to, i=last[x];i;i=e[i].nex){
        to = e[i].to;
        if(to != f && to != son[x]) dfs2(to, x, to);
    }
}

void build(int x, int l, int r, int k){
    t[x][k].l = l, t[x][k].r = r, t[x][k].size = r-l+1;
    if(l == r){
        if((pre[vis[l]]>>k) & 1) t[x][k].res1 = 1;
        return;
    }
    int mid = (l+r>>1);
    build(ll(x), l, mid, k), build(rr(x), mid+1, r, k); up(x, k);
}

void pushdown(int x, int k){
    if(t[x][k].tag == 0) return;
    t[ll(x)][k].tag ^= 1, t[ll(x)][k].res1 = t[ll(x)][k].size-t[ll(x)][k].res1;
    t[rr(x)][k].tag ^= 1, t[rr(x)][k].res1 = t[rr(x)][k].size-t[rr(x)][k].res1;
    t[x][k].tag = 0;
}

void update(int x, int l, int r, int k){
    if(l <= t[x][k].l && t[x][k].r <= r){
        t[x][k].res1 = t[x][k].size-t[x][k].res1;
        t[x][k].tag ^= 1;
        return;
    }
    int mid = (t[x][k].l+t[x][k].r>>1); pushdown(x, k);
    if(l <= mid) update(ll(x), l, r, k);
    if(mid < r) update(rr(x), l, r, k); up(x, k);
}

segment_tree query(int x, int l, int r, int k){
    if(l <= t[x][k].l && t[x][k].r <= r) return t[x][k];
    segment_tree temp; temp.size = temp.res1 = 0;
    if(r < t[x][k].l || t[x][k].r < l) return temp;
    pushdown(x, k); temp.size = t[x][k].size;
    return query(ll(x), l, r, k)+query(rr(x), l, r, k);
}

void modify(int x, int y, int v){
    if(dep[x] < dep[y]) swap(x, y);
    for(int i=0;i<=10;i++)
        if(((v^val[x])>>i) & 1) update(1, id[x], id[x]+size[x]-1, i);
    val[x] = v;
}

int ask(int xx, int yy){
    int x, y, res = 0; segment_tree temp, check;
    for(int i=0;i<=10;i++){
        x = xx, y = yy; temp.size = temp.res1 = 0;
        while(top[x] != top[y]){
            if(dep[top[x]] < dep[top[y]]) swap(x, y);
            temp = temp+(check=query(1, id[top[x]], id[x], i));
            x = fa[top[x]];
        }
        if(id[x] > id[y]) swap(x, y);
        temp = temp+(check=query(1, id[x], id[y], i));
        res += (temp.size-temp.res1)*temp.res1*(1<<i);
    }
    return res;
}

_int main(){
    int opt, x, y, z; n = gi(), m = gi();
    for(int i=1;i<n;i++) x = gi(), y = gi(), z = gi(), add(x, y, z), add(y, x,  z);
    dfs1(1, -1, 1); dfs2(1, -1, 1);
    for(int i=0;i<=10;i++) build(1, 1, n, i);

    for(int i=1;i<=m;i++){
        opt = gi(), x = gi(), y = gi();
        if(opt == 1) cout << ask(x, y) << endl;
        else z = gi(), modify(x, y, z);
    }
    return 0;
}

[洛谷P3401] 洛谷樹