[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章