1. 程式人生 > >P4332 [SHOI2014]三叉神經樹

P4332 [SHOI2014]三叉神經樹

不同 數據 自己的 出了 == 整體 大於 近日 葉子

\(\color{#0066ff}{ 題目描述 }\)

  1. 計算神經學作為新興的交叉學科近些年來一直是學術界的熱點。一種叫做SHOI 的神經組織因為其和近日發現的化合物 SHTSC 的密切聯系引起了人們的極大關註。

    SHOI 組織由若幹個 SHOI 細胞構成,SHOI 細胞之間形成嚴密的樹形結構。每個 SHOI 細胞都有且只有一個輸出端,被稱為軸突,除了一個特殊的、被稱為根細胞的 SHOI 細胞的輸出作為整個組織的輸出以外,其余細胞的軸突均連向其上級 SHOI 細胞;並且有且只有三個接收端,被稱為樹突,從其下級細胞或者其它神經組織那裏接收信息。SHOI 細胞的信號機制較為簡單,僅有 \(0\)\(1\)

    兩種。每個 SHOI 細胞根據三個輸入端中 \(0\)\(1\)信號的多寡輸出較多的那一種。

    現在給出了一段 SHOI 組織的信息,以及外部神經組織的輸入變化情況。請你模擬 SHOI 組織的輸出結果。

\(\color{#0066ff}{輸入格式}\)

  1. 輸入的第一行包含一個整數 \(n\)。表示 SHOI 組織的總細胞個數。SHOI 細胞由 \(1\)~ \(n\) 編號,編號為 \(1\) 的是根細胞。

    從第二行開始的 \(n\)行,每行三個整數 \(x_1, x_2, x_3\),分別表示編號為 \(1\)~ \(n\) 的 SHOI 細胞的樹突連接。\(1 < x_i \leq n\)

    表示連向編號為 \(x_i\) 的細胞的軸突, \(n < x_i \leq 3n+1\) 表示連向編號為 \(x_i\) 的外界輸入。輸入數據保證給出的 SHOI 組織是合法的,且所有的 \(x_i\) 兩兩不同。

    接下來一行包含 \(2n+1\)\(0/1\) 的整數,表示初始時的外界輸入。

    \(n+3\)行有一個整數 \(q\),表示總操作數。

    之後 \(q\) 行每行一個整數 \(x\),表示編號為 \(x\) 的外界輸入發生了變化。

\(\color{#0066ff}{輸出格式}\)

輸出 \(q\) 行,每行一個整數,對應第 \(i\) 次外界輸入變化後的根細胞的輸出。

\(\color{#0066ff}{輸入樣例}\)

3
2 3 4
5 6 7
8 9 10
0 0 0 0 1 1 1
5
4
4
5
6
8

\(\color{#0066ff}{輸出樣例}\)

1
0
0
1
1

\(\color{#0066ff}{數據範圍與提示}\)

對於 \(10 \%\)的數據, \(n \leq 1000, q \leq 1000\)

對於 \(30 \%\)的數據, \(n \leq 100000, q \leq 100000\)

對於 \(100 \%\)的數據, \(n \leq 500000, q \leq 500000\)

\(\color{#0066ff}{ 題解 }\)

這毒瘤題讀了半天沒看懂啥意思,現在簡單概括一下題意

給你一顆三叉樹(類比二叉樹qwq),1為根,前n個點每個點有三個孩子,\(n+1\to3n\)這些點為葉子節點,然後給你所有葉子的權值0/1,定義\(1-n\)點的權值為,三個孩子中權為1的孩子的個數是否大於1,如果是則權值為1,否則為0。現在給你一些操作,每次讓一個葉子的權值異或1,每次操作輸出根的權值

我們考慮每次操作對答案的影響,設\(v_i\)為i有多少個權值為1的孩子\(v_i\in [0,3]\)

如果當前點權值由0變1,那麽什麽情況下父親的權值也會改變呢

顯然當且僅當父親的v=1,那麽加上自己的改變, 父親的v變為2,這時父親的權值也從原來的0變為1,這就是一個子問題了

同理,如果是1變0, 當且僅當父親的v=2時父親也會改變

那麽,我們可以得到一個結論,每次修改一個點產生的影響是從這個點向上一條連續v為1或2的鏈的權值的改變,只要這個鏈包括了1, 答案就會改變

那麽現在的問題是,怎麽快速維護這個東西,我們需要快速找到一個點向上延伸1/2的最上面那個點,還有一條鏈的權值的修改

可以用LCT維護這個東西, 顯然不需要makeroot操作,那麽翻轉標記就沒用了

每個點維護兩個點的指針,一個是最深的v!=1的點,一個是最深的v!=2的點

這個點實際上就是延伸的鏈頂的父親

為什麽要最深? 因為要保證下面的1/2連續

怎麽維護? 因為要最深,所以在splay上,先考慮右兒子,然後是自己,最後是左兒子

如果這個點存在,那麽就對答案沒有影響(仔細想想),我們把這個點轉上去,那麽實際上就是當前點的單點修改,右子樹整體的修改,這個打標記即可,註意v改變了,n1和n2要互換!

如果不存在呢,說明此鏈一直延伸到根,那麽直接對整個splay打標記,並把答案修改即可

這題就沒了。。。

發現實際上每個點的權值就是\(\frac v 2\),所以直接維護v就行了

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 5e5 + 3;
const int maxm = 1.5e6 + 3;
struct node {
    node *ch[2], *fa, *n1, *n2;
    int val, tag;
    node(int val = 0, int tag = 0): val(val), tag(tag) {
        ch[0] = ch[1] = fa = n1 = n2 = NULL;
    }
    void trn() { std::swap(n1, n2); val ^= 3; tag ^= 1; }
    void dwn() {
        if(!tag) return;
        if(ch[0]) ch[0]->trn();
        if(ch[1]) ch[1]->trn();
        tag = 0;
    }
    void upd() {
        n1 = n2 = NULL;  //這行不能少!
        //右根左這樣找
        if(ch[1]) n1 = ch[1]->n1;
        if(!n1) n1 = this->val == 1? NULL : this;
        if(ch[0] && !n1) n1 = ch[0]->n1;
        if(ch[1]) n2 = ch[1]->n2;
        if(!n2) n2 = this->val == 2? NULL : this;
        if(ch[0] && !n2) n2 = ch[0]->n2;
    }
    bool isr() { return this == fa->ch[1]; }
    bool ntr() { return fa && (fa->ch[1] == this || fa->ch[0] == this); }
}pool[maxn];
int fa[maxm], val[maxm], du[maxm];
int n, ans;
void toposort() {
    std::queue<int> q;
    for(int i = n + 1; i <= 3 * n + 1; i++) val[i] = in() << 1, q.push(i);
    while(!q.empty()) {
        int tp = q.front(); q.pop();
        if(tp <= n) pool[tp].upd(), pool[tp].fa = pool + fa[tp];
        pool[fa[tp]].val += (tp > n? val[tp] : pool[tp].val) >> 1;
        du[fa[tp]]--;
        if(!du[fa[tp]]) q.push(fa[tp]);
    }
    pool[1].fa = NULL;
}
void rot(node *x) {
    node *y = x->fa, *z = y->fa;
    bool k = x->isr(); node *w = x->ch[!k];
    if(y->ntr()) z->ch[y->isr()] = x;
    (x->ch[!k] = y)->ch[k] = w;
    (y->fa = x)->fa = z;
    if(w) w->fa = y;
    y->upd(), x->upd();
}
void splay(node *o) {
    static node *st[maxn];
    int top;
    st[top = 1] = o;
    while(st[top]->ntr()) st[top + 1] = st[top]->fa, top++;
    while(top) st[top--]->dwn();
    while(o->ntr()) {
        if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
        rot(o);
    }
}

void access(node *x) {
    for(node *y = NULL; x; x = (y = x)->fa)
        splay(x), x->ch[1] = y, x->upd();
}

int main() {
    n = in();
    for(int i = 1; i <= n; i++) du[fa[in()] = fa[in()] = fa[in()] = i] = 3;
    toposort();//求初始的答案
    ans = pool[1].val >> 1;
    int x, tp;
    for(int T = in(); T --> 0;) {
        tp = (val[x = in()] ^= 2) >> 1;
        access(pool + (x = fa[x])), splay(pool + x);  
        node *o = pool + x;
        node *nr = tp? o->n1 : o->n2;  //找最深的點
        if(nr) {
            splay(nr);
            if(nr->ch[1]) nr->ch[1]->trn(), nr->ch[1]->upd();
            nr->val += tp? 1 : -1;
            nr->upd();
        }
        else ans ^= 1, o->trn(), o->upd();
        printf("%d\n", ans);
    }
    return 0;
}

P4332 [SHOI2014]三叉神經樹