• 牛客 情报传递 树剖+主席树


    题目链接:https://ac.nowcoder.com/acm/problem/20570

      要统计X到Y上的点的数量以及危险值大于C的点的数量,对树上两点的路径进行查询可以用树链剖分,在某一个时间点t,我们要求出X到Y路径上危险值大于C的点的数量,我们可以把它转化为求在时间t-C-1之前开始收集情报的情报员数量,因为一旦开始收集危险值就会一直增加,所以在时间t-C-1之前开始收集情报的点的危险值一定是大于C的。

      这里我们可以使用主席树,我的做法是对每一个时间点都用动态开点建一棵线段树,如果k==2,那么在时间点为t的这颗树上,情报员T的值要赋值为1,说明情报员从t时间点开始收集情报了,如果k==1,那么我们需要的是查找,但是我这里因为要每一个时间点都创建一棵线段树,所以我们可以让root[t]=root[t-1],即让时间点为t的这个树指向时间点为t-1的这颗树的根节点,这样两颗树就是一模一样的了。然后再让时间点为0和t-C-1的这两颗线段树相减,两颗树两两对应的点相减,类似于求前缀和,我们只要用树剖查找X和Y路径上所有的点在两颗树相减之后得到的线段树(这颗线段树代表的时间就是1到t-C-1这一段时间)中值为1的个数就行了。

    代码:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<map>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<set>
    #include<cstdio>
    #include<string>
    #include<deque> 
    using namespace std;
    typedef long long ll;
    #define eps 1e-8
    #define INF 0x3f3f3f3f
    #define maxn 200005
    inline int read(){
        int f=1,x=0;char ch;
        do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
        do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
        return f*x;
    }
    struct node{
        int v,next;
    }edge[maxn*2];
    int n,m,t,cnt,boss,step,num,ans1,ans2;
    struct Tree{
        int l,r;
        int sum;
    }tree[maxn*25];
    int head[maxn],fa[maxn],son[maxn],top[maxn],tot[maxn],L[maxn],dep[maxn],root[maxn];
    void init(){
        memset(head,-1,sizeof(head));
        cnt=step=num=0;
    }
    void add(int u,int v){
        edge[++cnt].v=v;
        edge[cnt].next=head[u];
        head[u]=cnt;
    }
    void dfs(int u,int pre,int depth){
        dep[u]=depth;
        fa[u]=pre;
        tot[u]=1;
        int mx=0;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v==pre) continue;
            dfs(v,u,depth+1);
            tot[u]+=tot[v];
            if(tot[v]>mx){
                mx=tot[v];
                son[u]=v;
            }
        }
    }
    void dfs2(int u,int pre){
        L[u]=++step;
        top[u]=pre;
        if(son[u])
        dfs2(son[u],pre);
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(top[v]==0)
            dfs2(v,v);
        }
    }
    void update(int pre,int &root,int l,int r,int index){
        root=++num;
        tree[root]=tree[pre];
        tree[root].sum++;
        if(l==r) return;
        int mid=(l+r)/2;
        if(index<=mid)
        update(tree[pre].l,tree[root].l,l,mid,index);
        else
        update(tree[pre].r,tree[root].r,mid+1,r,index);
    } 
    int ask_interval(int root,int l,int r,int L,int R){ 
        if(L>R) return 0;
        if(l>=L&&r<=R){
            return tree[root].sum;
        }
        int mid=(l+r)/2;
        int ans=0;
        if(L<=mid)
        ans+=ask_interval(tree[root].l,l,mid,L,R);
        if(mid<R)
        ans+=ask_interval(tree[root].r,mid+1,r,L,R);
        return ans;
    }
    void ask(int x,int y,int c,int now){
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            //只要是在时间[0,now-c-1)之间开始收集情报的情报员,都是危险的,这里没有写root[0],默认时间左边界就是0 
            if(now>c+1)
            ans2+=ask_interval(root[now-c-1],1,n,L[top[x]],L[x]);
            ans1+=L[x]-L[top[x]]+1;//dfs序相减的差就是这一段路径上的点的数量 
            x=fa[top[x]];
        }
        if(dep[x]>dep[y]) swap(x,y);
        if(now>c+1)
        ans2+=ask_interval(root[now-c-1],1,n,L[x],L[y]);
        ans1+=L[y]-L[x]+1;
    }
    int main()
    {
        n=read();
        init();
        int u;
        for(int i=1;i<=n;i++){
            u=read();
            if(u==0)
            boss=i;
            else{
                add(u,i);
                add(i,u);
            }
        }
        dfs(boss,0,1);
        dfs2(boss,boss);
        m=read();
        int X,Y,C,K,T;
        //每个时间点都建一棵树,如果这个时间点有一个情报员开始收集情报,那么就把他在树上相应的dfs序编号节点加1 
        for(int i=1;i<=m;i++){
            K=read();
            if(K==1){
                X=read();
                Y=read();
                C=read();
                root[i]=root[i-1];//建一棵树,但是这个时间点建的树和前一个时间点的线段树是一模一样的 
                ans1=ans2=0;
                ask(X,Y,C,i);//查找一下X到Y需要的两个答案 
                printf("%d %d
    ",ans1,ans2);
            }else{
                T=read();//T开始收集情报了,把它在树上对应的节点标记为1 
                update(root[i-1],root[i],1,n,L[T]);//L[T]节点加1,表示这个位置在i时间点开始计数了 
            }
        }
        return 0;
    }
  • 相关阅读:
    社区专家谈 12306
    一致性Hash算法(分布式算法)
    ASP.NET二级域名站点共享Session状态
    树莓派2 安装 win10Iot 和 Ubuntu mate
    Maven运行时异常java.lang.UnsupportedClassVersionError的解决方案
    Ubuntu安装steam游戏平台的解决方案
    Spring配置JNDI的解决方案
    Intellij Idea无法从Controller跳转到视图页面的解决方案
    电话激活windows server 2012的解决方案
    如何下载Red Hat Enterprise Linux系统
  • 原文地址:https://www.cnblogs.com/6262369sss/p/11754649.html
Copyright © 2020-2023  润新知