[JZOJ5157]沒有上司的舞會

題目大意

一棵樹,一開始只有0 號節點,有q 次操作,每次都會在某一個節點接上一個新的節點。
在每次操作結束後,請輸出這棵樹的最大獨立集。
本題強制在線。

1n3×105


題目分析

做這題時我採用的是immortalCO神犇的UOJBlog《基於變換合併的樹上動態 DP 的鏈分治算法》裏面的想法。
考慮樹上最大獨立集的dp 算法,令fx 表示第x 個點的子樹的最大獨立集,gx 表示第x 個點的子樹必須不選第x 個點的最大獨立集。
那麼我們有fx=max(gx,yson(x)gy+1) ,gx=yson(x)fy
可是這樣很難做動態修改類的問題,怎麼辦呢?我們考慮對樹進行輕重邊劃分,然後令h(x) 表示節點x 的重兒子,lfx 表示節點x 所有輕兒子的f 值之和,lgx 類似。
改寫一下dp 式子,我們有fx=max(gx,lgx+gh(x)+1) ,gx=lfx+fh(x)
考慮將這個轉移寫成矩陣的形式,重定義加法 和乘法運算

abab=max(a,b)=a+b

定義這兩個運算的零元是 ,可以發現這樣是滿足零元的性質的。
然後重定義矩陣乘法
ci,j=k=1nai,kbk,j

那麼我們轉移就相當於這樣一個矩陣乘法:
(lfxlfxvxlgx)×(fh(x)gh(x))=(lfxfh(x)vxlgxgh(x)lfxfh(x))

其中前面那個矩陣就是轉移矩陣,右邊的矩陣是由重兒子的信息組成的。
我們使用link-cut-tree維護一條重鏈上的所有轉移矩陣的乘積,我們可以利用區間矩陣積左乘上鍊底dp 信息得到的矩陣來得到任意一個位置的dp 值。
LCT輕重邊切換的時候要更新一下轉移矩陣。
總體實現細節比較多。
時間複雜度O(23nlogn)
不過這題其實是有基於鏈分治的一個更好實現也更快的O(22nlogn) 算法。

代碼實現

#include <algorithm>
#include <iostream>
#include <cctype>
#include <cstdio>

using namespace std;

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int buf[30];

inline void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    for (;x;x/=10) buf[++buf[0]]=x%10;
    if (!buf[0]) buf[++buf[0]]=0;
    for (;buf[0];putchar('0'+buf[buf[0]--]));
}

const int N=300050;

inline int plu(int x,int y){return x>y?x:y;}

inline int mult(int x,int y){return (x<0||y<0)?-1:x+y;}

struct matrix
{
    int num[2][2];
}I,ret;

inline matrix operator*(matrix x,matrix y)
{
    ret.num[0][0]=plu(mult(x.num[0][0],y.num[0][0]),mult(x.num[0][1],y.num[1][0]));
    ret.num[0][1]=plu(mult(x.num[0][0],y.num[0][1]),mult(x.num[0][1],y.num[1][1]));
    ret.num[1][0]=plu(mult(x.num[1][0],y.num[0][0]),mult(x.num[1][1],y.num[1][0]));
    ret.num[1][1]=plu(mult(x.num[1][0],y.num[0][1]),mult(x.num[1][1],y.num[1][1]));
    return ret;
}

int f[N],g[N],lf[N],lg[N];
bool is_online;
int n,q,ans;

namespace link_cut_tree
{
    int size[N],fa[N],par[N],LEFT[N],RIGHT[N];
    matrix v[N],mul[N];
    int son[N][2];

    bool side(int x){return son[fa[x]][1]==x;}

    void update(int x)
    {
        size[x]=size[son[x][0]]+size[son[x][1]]+1,LEFT[x]=son[x][0]?LEFT[son[x][0]]:x,RIGHT[x]=son[x][1]?RIGHT[son[x][1]]:x;
        if (son[x][0]) mul[x]=mul[son[x][0]]*v[x];
        else mul[x]=v[x];
        if (son[x][1]) mul[x]=mul[x]*mul[son[x][1]];
    }

    void rotate(int x)
    {
        int y=fa[x];bool s=side(x);
        if (fa[y]) son[fa[y]][side(y)]=x;
        if (son[x][s^1]) fa[son[x][s^1]]=y;
        son[y][s]=son[x][s^1],son[x][s^1]=y;
        fa[x]=fa[y],fa[y]=x;
        if (par[y]) par[x]=par[y],par[y]=0;
        update(y)/*,update(x)*/;
    }

    void splay(int x,int y)
    {
        for (;fa[x]!=y;rotate(x))
            if (fa[fa[x]]!=y)
                if (side(x)==side(fa[x])) rotate(fa[x]);
                else rotate(x);
        update(x);
    }

    matrix calc(int x)
    {
        ret.num[0][0]=ret.num[1][0]=-1;
        ret.num[1][1]=lf[x],ret.num[0][1]=plu(lf[x],mult(1,lg[x]));
        return ret;
    }

    int getlight(int x,int y)
    {
        int z;
        splay(y=LEFT[y],x),splay(z=RIGHT[y],x);
        matrix ret=calc(z);
        ret=mul[son[z][0]]*ret,f[y]=ret.num[0][1],g[y]=ret.num[1][1],lf[x]+=f[y],lg[x]+=g[y];
        splay(y,x),v[x].num[0][0]=v[x].num[1][0]=lf[x],v[x].num[0][1]=mult(1,lg[x]),v[x].num[1][1]=-1;
        return update(x),y;
    }

    int getheavy(int x,int y)
    {
        int z;
        splay(y=LEFT[y],0),splay(z=RIGHT[y],0);
        matrix ret=calc(z);
        ret=mul[son[z][0]]*ret,f[y]=ret.num[0][1],g[y]=ret.num[1][1],lf[x]-=f[y],lg[x]-=g[y];
        splay(y,0),v[x].num[0][0]=v[x].num[1][0]=lf[x],v[x].num[0][1]=mult(1,lg[x]),v[x].num[1][1]=-1;
        return update(x),y;
    }

    int access(int x)
    {
        int nxt=0;
        for (;x;update(nxt=x),x=par[x])
        {
            splay(x,0);
            if (son[x][1]) getlight(x,son[x][1]),fa[son[x][1]]=0,par[son[x][1]]=x;
            if (nxt) nxt=getheavy(x,nxt),fa[nxt]=x,par[nxt]=0;
            son[x][1]=nxt;
        }
        return nxt;
    }

    void addpoint(int x)
    {
        if (x) access(x),splay(x,0),++lf[x],v[x].num[0][0]=v[x].num[1][0]=lf[x],update(x);
        ++n,f[n]=1,g[n]=lf[n]=lg[n]=0,par[n]=x,v[n]=calc(n),update(n),access(n),splay(n,0);
    }

    int query(){return access(1),calc(1).num[0][1];}
};

int main()
{
    freopen("party.in","r",stdin),freopen("party.out","w",stdout);
    using namespace link_cut_tree;
    I.num[0][0]=I.num[1][1]=0,I.num[0][1]=I.num[1][0]=-1,mul[0]=v[0]=I;
    q=read(),is_online=read(),addpoint(0);
    for (int i=1,x;i<=q;++i) addpoint((read()^(is_online*ans))+1),write(ans=query()),putchar('\n');
    fclose(stdin),fclose(stdout);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章