每次修改一个点的黑白状态,询问树上最远黑点距离
拿这个题做动态点分治模板题:(%%%PoPoQQQ大爷)
点分治的过程是对树块找重心之后分成多个小树块,降低规模分别处理的过程,把链的信息收到其中“最高重心”上,从所有的重心处像分治中的不同子树索取到重心的链,就可以覆盖所有链的信息。动态点分治就像把序列分治变成线段树一样,在分治的架子上加了信息维护,实现树链信息维护与查询。
需要什么?
每个重心需要其每个分离子树到它的信息(很重要,否则形成链的重复部分,并且还需要一个自己到自己的空信息维护单链上来的信息)
每个重心需要它到父分治块的信息
全局需要维护每个重心的信息
因此,考虑问题的静态版本,需要维护每个节点每个子树内的最长链,那么为了修改,最大化应换成堆维护。
每个重心维护一个堆,表示其分离子树中每个到它的最长链
每个重心维护一个堆,表示其块内到父重心的最长链
全局维护一个堆,表示每个重心处的最长链
修改时只要从一个节点作为重心的块开始向上修改,就可以遍历到所有包含它的块,修改到父重心的信息,修改父重心的信息
注意开始时的节点的分离子树那个堆要插一个0表示单链,而改掉节点值的时候0的存在性也要相应地变化。
堆也比较有技巧:封装双堆分别为值和删了的值,取的时候两堆顶相同则pop,相当于变种删除标记
LCA倍增写错:要返回p[x][0](QAQ)
#include
#include
#include
using namespace std;
const int Mn=100005;
int cnt=0,h[Mn],n,m,vst[Mn],maxx,tg,s[Mn],sz,prt[Mn];
int d[Mn],p[Mn][21],co[Mn];
struct Priority_Queue{
priority_queueq,del;
void push(int x){q.push(x);}
void erase(int x){del.push(x);}
int top(){
while(del.size()&&del.top()==q.top())
{del.pop();q.pop();}
return q.top();
}
void Pop(){
while(del.size()&&del.top()==q.top())
{del.pop();q.pop();}
q.pop();
}
int sec_top(){
int tmp=top();Pop();
int se=top();push(tmp);
return se;
}
int size(){return q.size()-del.size();}
}c[Mn],f[Mn],ans;
struct Edge{int to,next;}w[Mn*2];
void AddEdge(int x,int y){w[++cnt]=(Edge){y,h[x]};h[x]=cnt;}
void DFS(int x,int fa){
int j,y;
p[x][0]=fa;
for(j=1;j<=20;j++)p[x][j]=p[p[x][j-1]][j-1];
for(j=h[x];j;j=w[j].next){
y=w[j].to;
if(y==fa)continue;
d[y]=d[x]+1;
DFS(y,x);
}
}
void Insert(Priority_Queue &s){
if(s.size()>1)ans.push(s.top()+s.sec_top());
}
void Erase(Priority_Queue &s){
if(s.size()>1)ans.erase(s.top()+s.sec_top());
}
void init(){
int i,x,y;
scanf("%d",&n);
for(i=1;imax(mx,sz-s[x])){
maxx=max(mx,sz-s[x]);
tg=x;
}
}
int FindCen(int x){
maxx=n+1;tg=0;
DP(x,0);
sz=s[x];
Biggest(x,0);
return tg;
}
int LCA(int x,int y){
int j;
if(d[x]=0;j--)
if(d[p[x][j]]>=d[y])x=p[x][j];
if(x==y)return x;
for(j=20;j>=0;j--)
if(p[x][j]!=p[y][j])
{x=p[x][j];y=p[y][j];}
return p[x][0];
}
int Dis(int x,int y){return d[x]+d[y]-2*d[LCA(x,y)];}
void work(int x,int fa,int Gra){
int j,y;
f[Gra].push(Dis(x,prt[Gra]));
for(j=h[x];j;j=w[j].next){
y=w[j].to;
if(y==fa||vst[y])continue;
work(y,x,Gra);
}
}
int DivOnT(int x,int fa){
int j,y,G,Gy;
G=FindCen(x);prt[G]=fa;
work(G,0,G);
vst[G]=1;
c[G].push(0);
for(j=h[G];j;j=w[j].next){
y=w[j].to;
if(!vst[y]){
Gy=DivOnT(y,G);
c[G].push(f[Gy].top());
}
}
Insert(c[G]);
return G;
}
void Light(int x){
Erase(c[x]);
c[x].erase(0);
Insert(c[x]);
for(int y=x;prt[y];y=prt[y]){
Erase(c[prt[y]]);
if(f[y].size())c[prt[y]].erase(f[y].top());
f[y].erase(Dis(x,prt[y]));
if(f[y].size())c[prt[y]].push(f[y].top());
Insert(c[prt[y]]);
}
}
void LiOut(int x){
Erase(c[x]);
c[x].push(0);
Insert(c[x]);
for(int y=x;prt[y];y=prt[y]){
Erase(c[prt[y]]);
if(f[y].size())c[prt[y]].erase(f[y].top());
f[y].push(Dis(x,prt[y]));
if(f[y].size())c[prt[y]].push(f[y].top());
Insert(c[prt[y]]);
}
}
void solve(){
int i,x;char ch[5];
cnt=n;
scanf("%d",&m);
for(i=1;i<=m;i++){
scanf("%s",ch);
if(ch[0]=='G'){
if(cnt<=1)printf("%d\n",cnt-1);
else printf("%d\n",ans.top());
}
else{
scanf("%d",&x);
if(!co[x]){cnt--;Light(x);co[x]=1;}
else{cnt++;LiOut(x);co[x]=0;}
}
}
}
int main(){
init();
DivOnT(1,0);
solve();
return 0;
}