洛谷P4332 [SHOI2014]三叉神經樹(LCT,樹剖,二分查找,拓撲排序)

洛谷題目傳送門ui

你谷無題解因而來補一發spa

隨便百度題解,發現了很多諸如樹剖\(log^3\)LCT\(log^2\)的可怕描述。。。。。。code

因而來想一想怎麼利用題目的性質,把複雜度降下來。排序

首先,每一個點的輸出狀態只有\(0/1\),因而每一個點的總狀態也很是有限,能夠根據權值爲\(1\)的兒子數量\(0-3\)分爲四種,記爲該點的點權。ip

咱們都會模擬暴力過程——先改葉子節點(先默認爲\(0\)改成\(1\)),若是它的父親此時權值爲\(1\)的兒子數量從原來小於\(0\)的變成大於\(0\)的,那麼父親的權值也要改。以此類推,直到有一個節點輸出狀態沒有變化,那麼它的全部祖先確定不會變。get

經過模擬咱們發現,每次修改的必定是一段自底向上的連續區間!it

接着也就不難想到,只有當點權爲\(1\)時,才能經過修改點權變成\(2\),使輸出由\(0\)變成\(1\),從而繼續引起祖先的變化。那麼咱們須要知道的就是,對於每個葉子節點,它自底向上的連續一段點權爲\(1\)的部分。io

再討論葉子節點\(1\)\(0\)的狀況,同理也能夠發現咱們還要維護自底向上的連續一段點權爲\(2\)的部分。ast

這個能夠樹剖(有不少維護法,都是\(log^2\)的,跳鏈和鏈修改都有\(log\))正在學樹剖,先留個坑,到時候再補。。。class

固然能夠LCT,講兩個維護法。第一種是用bool值維護區間是否有權值不爲\(1/2\)的點,每次Splay上二分查找最深的不爲\(1/2\)的點,把它伸展上來,右子樹作區間修改,這個點作單點修改。

由於寫二分比較麻煩(其實就是幾行的事),因此還不如直接維護最深的不爲\(1/2\)點的編號,找都不用找。直接把它伸展上來。修改同上。容易發現這裏的LCT連換根都不要。

兩種寫法都須要注意特判:若是整條從根到葉子的鏈沒有一個不爲\(1/2\)的點,直接作區間修改。

分享一個naive的錯誤——蒟蒻默認父節點的編號比子節點小,而後pushup直接取\(\max\),居然得到了95分?!調了半天本機對拍又是全AC(本身的數據生成器確定是父節點的編號比子節點小啦。。。)

剛掉這題後還收穫了一點小經驗——不要給LCT永久化地貼上常數大的標籤!由於少一個\(log\),因此\(n\)越大越有優點(這題\(5*10^5\)),還不用reverse。看看統計,就知道什麼叫LCT全方位(時間、空間、碼量)完爆樹剖的感受了哈哈哈哈hhhh

https://www.luogu.org/recordnew/lists?uid=&pid=P4332&status=&sort=1

https://loj.ac/problem/2187/statistics/fastest

#include<cstdio>
#include<algorithm>
#define RG register
#define I inline
#define R RG int
#define lc c[x][0]
#define rc c[x][1]
#define G if(++ip==ie)if(fread(ip=ibuf,1,L,stdin))
using namespace std;
const int N=5e5+9,M=1.5e6+9,L=1<<19;
char ibuf[L],*ie=ibuf+L,*ip=ie-1;
int n,f[M],c[N][2],t[N],n1[N],n2[N],v[M],q[M],d[N];
I int max(R x,R y){return x>y?x:y;}
I int in(){
    G;while(*ip<'-')G;
    R x=*ip&15;G;
    while(*ip>'-'){(x*=10)+=*ip&15;G;}
    return x;
}
I bool nrt(R x){
    return c[f[x]][0]==x||c[f[x]][1]==x;
}
I void up(R x){//先右兒子再本身最後左兒子
    if(!(n1[x]=n1[rc])&&!(n1[x]=x*(v[x]!=1)))n1[x]=n1[lc];
    if(!(n2[x]=n2[rc])&&!(n2[x]=x*(v[x]!=2)))n2[x]=n2[lc];
}
I void dn(R x,R tg){//被區間修改的要麼都是1要麼都是2,直接反轉信息
    v[x]^=3;swap(n1[x],n2[x]);t[x]+=tg;
}
I void all(R x){
    if(nrt(x))all(f[x]);
    if(t[x])dn(lc,t[x]),dn(rc,t[x]),t[x]=0;
}
I void rot(R x){
    R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
    if(nrt(y))c[z][c[z][1]==y]=x;
    f[f[f[c[c[x][!k]=y][k]=w]=y]=x]=z;up(y);
}
I void sp(R x){
    all(x);
    for(R y;nrt(x);rot(x))
        if(nrt(y=f[x]))rot((c[f[y]][0]==y)^(c[y][0]==x)?x:y);
    up(x);
}
I void ac(R x){
    for(R y=0;x;sp(x),rc=y,up(y=x),x=f[x]);
}
int main(){
    n=in();R he,tl=0,i,x,tp,nowrt;//nowrt全局記錄根的輸出,方便,減少常數
    for(i=1;i<=n;++i)d[f[in()]=f[in()]=f[in()]=i]=3;
    for(;i<=3*n+1;++i)v[q[++tl]=i]=in()<<1;
    for(he=1;he<=tl;++he){//懶得dfs了,直接從下往上拓撲排序預處理
        x=q[he];if(x<=n)up(x);
        v[f[x]]+=v[x]>>1;
        if(!--d[f[x]])q[++tl]=f[x];
    }
    nowrt=v[1]>>1;
    for(R q=in();q;--q){
        tp=(v[x=in()]^=2)-1;//記錄當前變化類型
        ac(x=f[x]);sp(x);
        if((~tp?n1:n2)[x]){
            sp(x=(~tp?n1:n2)[x]);
            dn(rc,tp),up(rc);
            v[x]+=tp;up(x);
        }
        else dn(x,tp),up(x),nowrt^=1;//注意特判
        putchar(nowrt|'0');putchar('\n');
    }
    return 0;
}
相關文章
相關標籤/搜索