bzoj1095动态点分治

写在正文前的例行吐槽:

我终于,终于,终于也是写过动态点分治的人了。。。

加上动态dp和动态树是不是就可以集齐套装召唤神龙了呢。。(思考ing)

动态点分治:

你会碰到一类问题。。有多个修改询问之类的操作。。可以用一次点分治回答一次询问

这时候我们可以把点分树建出来。。。

点分树有一些性质:

1.树高不超过log层。(显然吧orz。。。)

2.在进行点分治时,套路就是从当前点出发向外扩展,直到碰到已经访问过的点为止,然后这样可以处理出所有从这个点出发的、还未被统计过的链。我们将从点i出发扩展到的区域称为它的管辖范围(随便yy的),显然每个点的管辖范围都是一定的。

而在点分树上,一个点的儿子们管辖的范围绝对没有重合。

然后就在每个点上维护一下它管辖的范围的信息,且儿子的信息基本上都会对父亲产生影响,所以每次修改时暴力往上一个个修改父亲的信息直到走到点分树的根为止,修改一次是(log n*修改复杂度)的。而因为在点分治合并时往往要去掉同一个子树内的重复信息,所以像bzoj1095一样在每个点上维护它的所有儿子的信息也比较常见了。(儿子们分开了避免合并出问题)

bzoj1095具体题解:

在每个点上记录:

它的管辖范围内所有黑点到它点分树上父亲的距离(堆1)

它的每个儿子的堆1的堆顶(堆2)

于是:每个点管辖范围内经过它的最长链就是它堆2中取出最大值和次大值相加,然后把每个点的这个信息扔到ans堆里,查询时直接找出ans堆的堆顶就好了。

其实参考一下本来点分的过程就发现了神奇的事情:

对于每个点,

用数组存下它在点分树上儿子的管辖范围内所有黑点到它的距离 ,然后取出它的最大值->距离会修改,就用堆存(堆1)

每个儿子要分开存,不然合并信息时会出现同一个子树里的合并在一起的情况,而每个儿子的最大值可能会被修改,要求所有儿子的最大值的最大值和次大值(。。。)->本来是两个int记录一下的东西,因为动态了又变成堆(堆2)

真是妙不可言啊。。。

当然,因为这个堆还要支持删除,而我懒得手写堆,也不是很想在找最大值和次大值的时候pop两次再push回来,所以直接用了multiset,常数当然是上天了的。。。在luogu里T了一个点,bzoj上过了。为了代码短而成为了咸鱼。。。orz。

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (register int x=y; x<=z; x++)
#define downrep(x,y,z) for (register int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (register int x=hed[y]; ~x; x=edge[x].nex)
inline int read(){
    int x=0; int w=0; char ch=0;
    while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
    while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return w? -x:x;
}
const int N=100005;
const int Maxlg=17;
int tin[N],tout[N],tot,dep[N],f[N][Maxlg+1],fa[N],n,m,nedge,hed[N];
int nsz,root,sz[N],maxson[N],vis[N],col[N];
multiset<int> s1[N],s2[N],ans;
char s[15];
struct Edge{ int to,nex; }edge[N<<1];
void addedge(int a,int b){ edge[nedge].to=b; edge[nedge].nex=hed[a]; hed[a]=nedge++; }
void getdep(int k,int pre){
    tin[k]=++tot;
    repedge(i,k){
        int v=edge[i].to; if (v==pre) continue;
        dep[v]=dep[k]+1; f[v][0]=k;
        getdep(v,k);
    }tout[k]=tot;
}
bool isancestor(int x,int y){ return ((tin[x]<=tin[y])&&(tout[y]<=tout[x])); }
int getlca(int x,int y){
    if(isancestor(x,y)) return x;
    if(isancestor(y,x)) return y;
    downrep(i,Maxlg,0)if (!isancestor(f[x][i],y)) x=f[x][i];
    return f[x][0];
}
int dis(int x,int y){ int xylca=getlca(x,y); return (dep[x]+dep[y]-2*dep[xylca]); }
void getsz(int k,int pre){
    ++nsz; repedge(i,k){ int v=edge[i].to; if ((vis[v])||(v==pre)) continue; getsz(v,k); }
}
void getroot(int k,int pre){
    sz[k]=1; maxson[k]=0;
    repedge(i,k){
        int v=edge[i].to; if ((vis[v])||(v==pre)) continue;
        getroot(v,k); sz[k]+=sz[v]; maxson[k]=max(maxson[k],sz[v]);
    }
    maxson[k]=max(maxson[k],nsz-sz[k]);
    if ((!root)||(maxson[k]<maxson[root])) root=k;
}
void dfs(int k,int pre,int K,int d){
    s1[K].insert(d); 
    repedge(i,k){ int v=edge[i].to; if ((v==pre)||(vis[v])) continue; dfs(v,k,K,d+1); }
}
int Ans(int x){
    if (s2[x].begin()==s2[x].end()) return -1;
    int cnt=0; int res=0;
    for(multiset<int>::iterator i=(--s2[x].end()); ; i--){
      if (cnt<2) ++cnt,res+=(*i); else break;
      if (i==s2[x].begin()) break;
    }
    return (cnt>=2)? res:((cnt==1)? 0:(-1));
}
void getans(int k,int f){
    fa[k]=f; vis[k]=1; s2[k].insert(0);
    repedge(i,k){
        int v=edge[i].to; if (vis[v]) continue;
        nsz=0; getsz(v,k); root=0; getroot(v,k);
        dfs(v,k,root,1); 
        s2[k].insert(*(--s1[root].end()));
        getans(root,k);
    }
}
void Upd(int x){
    col[x]^=1; 
    if (!col[x]){
        ans.erase(ans.lower_bound(Ans(x)));
        s2[x].erase(s2[x].lower_bound(0));
        ans.insert(Ans(x));
        for(int i=x; fa[i]; i=fa[i]){
            int y=dis(x,fa[i]); int yy=(*(--s1[i].end()));
            s1[i].erase(s1[i].lower_bound(y));
            if (y==yy){ 
               ans.erase(ans.lower_bound(Ans(fa[i])));
               s2[fa[i]].erase(s2[fa[i]].lower_bound(y));
               if (s1[i].begin()!=s1[i].end()) s2[fa[i]].insert(*(--s1[i].end()));
               ans.insert(Ans(fa[i]));
            }
        }
    }
    if (col[x]){
        ans.erase(ans.lower_bound(Ans(x)));
        s2[x].insert(0);
        ans.insert(Ans(x));
        for(int i=x; fa[i]; i=fa[i]){
            int y=dis(x,fa[i]); 
            ans.erase(ans.lower_bound(Ans(fa[i])));
            if (s1[i].begin()!=s1[i].end()) 
            s2[fa[i]].erase(s2[fa[i]].lower_bound(*(--s1[i].end())));
            s1[i].insert(y);
            s2[fa[i]].insert(*(--s1[i].end())); 
            ans.insert(Ans(fa[i]));
        }
    }
}
int main(){
    scanf("%d",&n); nedge=0; ms(hed,-1,hed);
    rep(i,1,n-1){ int a,b; scanf("%d%d",&a,&b); addedge(a,b); addedge(b,a); }
    f[1][0]=1; getdep(1,1); rep(j,1,Maxlg) rep(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
    nsz=n; root=0; getroot(1,1); getans(root,0); 
    rep(i,1,n){ ans.insert(Ans(i)); col[i]=1; }
    scanf("%d",&m);
    rep(i,1,m){
        int x; scanf("%s",&s);
        if (s[0]=='C'){ scanf("%d",&x); Upd(x);	}
        if (s[0]=='G') printf("%d\n",(*(--ans.end())));
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值