Qtree树链剖分

此举是为了重新回到CSDN,自己博客写炸了。
然后嗯,先是推广一下我们的oj

小白菜oj
oj很好,很适合初学者

今次的题目还是很难得。
传送门
具体看看代码吧,重点是ST表管理和普通树管理。

#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int bin[20],Log[200005];//bin[i]表示2的i次方,Log就是log
int n,m,G,cnt,dfn,sum,tott;
int tot[100005],f[100005],dep[100005];// f[i]数组表示以i为根之后剩下所有子树家族最大的子树的家族数目
//dep表示到一号节点的距离
int mn[18][200005],ys[100005],fa[100005];//mn[i][j]表示dfs序中从j到j+2^i-1的最小深度的点
bool v[100005],col[100005];//col表示当前这个点的颜色,1为黑色,0为白色
struct node{
    int x,y,c,next;
} a[200005];
int len,last[100005];
void ins(int x,int y,int c){
    len++;
    a[len].x=x;a[len].y=y;a[len].c=c;
    a[len].next=last[x];last[x]=len;
}
/*
做法。
对于每个节点我们维护两个堆(堆的堆顶最大)
每个节点的第一个堆维护所有子树内的节点到自己父亲节点的距离
第二个堆维护所有子节点第一个堆的堆顶(最大值)
那么相对于每一个节点来说,第二个堆的最大值加次大值就是子树内经过这个节点的最长链!!
全局维护一个堆,记录所有节点第二个堆的最大值和次大值的和。堆顶(最大值)就是答案
*/
struct heap{
    priority_queue<int> A,B;
    void push(int x){
        A.push(x);
    }
    void erase(int x){
        B.push(x);
    }
    void pop(){
        while(B.size()&&A.top()==B.top())
            A.pop(),B.pop();
        A.pop();
    }
    int top(){
        while(B.size()&&A.top()==B.top())
            A.pop(),B.pop();
        if(!A.size())return 0;
        return A.top();
    }
    int size(){
        return A.size()-B.size();
    }
    int stop(){
        if(size()<2)return 0;
        int x=top();pop();
        int y=top();push(x);
        return y;
    }
} A,B[110000],C[110000];
void dfs(int x,int fa){
    mn[0][++dfn]=dep[x];
    ys[x]=dfn;
    for(int k=last[x]; k; k=a[k].next){
        int y=a[k].y;
        if(y!=fa){
            dep[y]=dep[x]+a[k].c;
            dfs(y,x);
            mn[0][++dfn]=dep[x];//回溯,很重要,不加回溯会错,先思考,视频里会详细讲解。
        }
    }
}
/*
getrt
找到树的重心并存在G里。
树的重心是一个点a
删掉树的重心之后所剩子树家族最大的最小(好好理解这句话!!!)
*/
void getrt(int x,int fa){
    tot[x]=1;f[x]=0;
    for(int k=last[x]; k; k=a[k].next){
        int y=a[k].y;
        if(y!=fa&&v[y]==false){
            getrt(y,x);
            tot[x]+=tot[y];
            f[x]=max(f[x],tot[y]);
        }
    }
    f[x]=max(f[x],sum-tot[x]);
    if(f[x]<f[G])
        G=x;
}
//按照新的树去建父子关系 ,当前这一层的中心去连接下一层的重心
void divi(int x){
    v[x]=true;
    for(int k=last[x]; k; k=a[k].next){
        int y=a[k].y;
        if(v[y]==false){
            sum=tot[y];G=0;
            getrt(y,x);
            fa[G]=x;divi(G);
        }
    }
}
//求x到y路径上深度最小的点的深度(最近公共祖先的深度 ) 
int rmq(int x,int y){
    x=ys[x];y=ys[y];
    if(x>y)
        swap(x,y);
    int t=Log[y-x+1];
    return min(mn[t][x],mn[t][y-bin[t]+1]);
}
//求x到y之间的距离
int dis(int x,int y){
    return dep[x]+dep[y]-2*rmq(x,y);
}
//把v节点变成黑色,x对于f有影响
void turn_black(int f,int x){
    if(f==x){
        if(B[f].size()==1)
            A.push(B[f].top());
    }
    if(!fa[f])
        return;
    int ff=fa[f],D=dis(ff,x),tmp=C[f].top();
    C[f].push(D);
    if(D>tmp){
        int mx=B[ff].top()+B[ff].stop(),size=B[ff].size();
        if(tmp)
            B[ff].erase(tmp);
        B[ff].push(D);
        int now=B[ff].top()+B[ff].stop();
        if(now>mx)
        {
            if(size>=2) A.erase(mx);
            if(B[ff].size()>=2) A.push(now);
        }
    }
    turn_black(ff,x);
}
//把v节点变成白色,v对于u有影响
void turn_white(int f,int x){
    if(f==x){
        if(B[f].size()==1)
            A.erase(B[f].top());
    }
    if(!fa[f])
        return;
    int ff=fa[f],D=dis(ff,x),tmp=C[f].top();
    C[f].erase(D);
    if(D==tmp){
        int mx=B[ff].top()+B[ff].stop(),size=B[ff].size();
        B[ff].erase(D);
        if(C[f].top())
            B[ff].push(C[f].top());
        int now=B[ff].top()+B[ff].stop();
        if(now<mx){
            if(size>=2)
                A.erase(mx);
            if(B[ff].size()>=2)
                A.push(now);
        }
    }
    turn_white(ff,x);
}
int main(){
    bin[0]=1;
    for(int i=1;i<20;i++)
        bin[i]=bin[i-1]*2;
    Log[0]=-1;
    for(int i=1;i<=200000;i++)
        Log[i]=Log[i/2]+1;
    scanf("%d",&n);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++){
        int x,y,c;scanf("%d%d%d",&x,&y,&c);
        ins(x,y,c);ins(y,x,c);
    }
    dfs(1,0);
    // mn[i][j]表示j到j+2^i-1最小深度的点
    // ST表
    for(int i=1;i<=Log[dfn];i++)
        for(int j=1;j<=dfn;j++)
            if(j+bin[i]-1<=dfn)
                mn[i][j]=min(mn[i-1][j],mn[i-1][j+bin[i-1]]);
    //j到j+2^i-1的最小值等于min(前半部分最小值,后半部分最小值)
    G=0;f[0]=2147483647;
    sum=n;getrt(1,0);
    fa[G]=0;divi(G);
    for(int i=1;i<=n;i++)
        col[i]=1; //col等于1代表节点为黑色,等于0为白色
    for(int i=1;i<=n;i++){
        turn_black(i,i);
        tott++;//tott表示当前黑色节点的总数
    }
    char ch[2];
    scanf("%d",&m);
    while(m--){
        scanf("%s",ch+1);
        if(ch[1]=='A'){
            if(tott<=0)
                printf("They have disappeared.\n");
            else 
                printf("%d\n",A.top());//全局的堆顶即为答案
        } 
        else{
            int x;
            scanf("%d",&x);
            if(col[x])
                turn_white(x,x),tott--;
            else 
                turn_black(x,x),tott++;
            col[x]^=1;//变颜色
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值