题目大意
一棵树,一开始只有
0
号节点,有
在每次操作结束后,请输出这棵树的最大独立集。
本题强制在线。
1≤n≤3×105
题目分析
做这题时我采用的是immortalCO神犇的UOJBlog《基于变换合并的树上动态 DP 的链分治算法》里面的想法。
考虑树上最大独立集的
dp
算法,令
fx
表示第
x
个点的子树的最大独立集,
那么我们有
fx=max(gx,∑y∈son(x)gy+1)
,
gx=∑y∈son(x)fy
。
可是这样很难做动态修改类的问题,怎么办呢?我们考虑对树进行轻重边划分,然后令
h(x)
表示节点
x
的重儿子,
改写一下
dp
式子,我们有
fx=max(gx,lgx+gh(x)+1)
,
gx=lfx+fh(x)
考虑将这个转移写成矩阵的形式,重定义加法
⊕
和乘法运算
⊗
:
a⊕ba⊗b=max(a,b)=a+b
定义这两个运算的零元是 −∞ ,可以发现这样是满足零元的性质的。
然后重定义矩阵乘法
ci,j=⊕k=1nai,k⊗bk,j
那么我们转移就相当于这样一个矩阵乘法:
(lfxlfxvx⊗lgx−∞)×(−∞−∞fh(x)gh(x))=(−∞−∞lfx⊗fh(x)⊕vx⊗lgx⊗gh(x)lfx⊗fh(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;
}