題目大意
一棵樹,一開始只有
在每次操作結束後,請輸出這棵樹的最大獨立集。
本題強制在線。
題目分析
做這題時我採用的是immortalCO神犇的UOJBlog《基於變換合併的樹上動態 DP 的鏈分治算法》裏面的想法。
考慮樹上最大獨立集的
那麼我們有
可是這樣很難做動態修改類的問題,怎麼辦呢?我們考慮對樹進行輕重邊劃分,然後令
改寫一下
考慮將這個轉移寫成矩陣的形式,重定義加法
定義這兩個運算的零元是
然後重定義矩陣乘法
那麼我們轉移就相當於這樣一個矩陣乘法:
其中前面那個矩陣就是轉移矩陣,右邊的矩陣是由重兒子的信息組成的。
我們使用link-cut-tree維護一條重鏈上的所有轉移矩陣的乘積,我們可以利用區間矩陣積左乘上鍊底
LCT輕重邊切換的時候要更新一下轉移矩陣。
總體實現細節比較多。
時間複雜度
不過這題其實是有基於鏈分治的一個更好實現也更快的
代碼實現
#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;
}