牛客国庆集训派对Day6 I-清明梦超能力者黄YY (树链剖分+区间最值+区间染色)

链接:https://www.nowcoder.com/acm/contest/206/I
来源:牛客网
 

题目描述

黄YY是一个清明梦超能力者,同时也是一个记忆大师。他能够轻松控制自己在梦中的一切,在醒来之后还能清晰的记得梦中所有的细节,这让他的朋友们都十分羡慕。
又是一个晚上,黄YY又到了自己的梦中,并且随手造出了一棵有n个点的树,树上每个点有一个初始颜色0。为了让这棵树不那么单调,黄YY拿起了画笔在上面尽情上色。每一次上色可以用u,
v, c来描述,代表黄YY把u, v这条路径上的点都染色成了c。
正当黄YY开心的完成了m次染色,准备在早上醒来之时向朋友们炫耀。但现实中的黄YY由于过于兴奋滚到了床下,撞到了脑袋,在剧痛中醒来。由于脑部受到了严重创伤,黄YY对刚才梦境中发生的一切发生了严重的信息丢失。
但英俊潇洒的黄YY当然不希望自己的窘态被朋友们发现。为了证明自己还是那个清明梦超能力者,他希望告诉朋友们自己上色后每个节点的颜色。同时为了更进一步证明他还是个记忆大师,他希望干脆直接说出每个点在倒数第k次染色时的颜色。
当然,现在的黄YY已经成了弱智了,作为黄YY最亲密的朋友,你快来帮帮黄YY吧!

输入描述:

第一行三个整数n, m, k,代表树的点数,黄YY染色的次数,以及最后求颜色时,倒数的次数(1 ≤ n, m, k ≤ 100000)。
接下来n - 1行,每行u, v代表u, v两点之间有一条边。这里保证1 ≤ u, v
≤ n,且无重边与自环,是一棵标准的树。
接下来m行,每一行三个数字u, v, c代表黄YY在第这次用c颜色的画笔从u涂到了v。

输出描述:

一行$n$个数字,输出每个点倒数第$k$次染色时的颜色。如果本身不足$k$次,输出0。

示例1

输入

复制

3 3 2
1 2
2 3
1 2 1
2 3 2
1 3 3

输出

复制

1 2 2

说明

对于点1在第一次和第三次染色的时候分别被染色为1, 3,倒数第二次的颜色就是1。
对于点2在第一、二、三次染色的时候分别被染色为1, 2, 3,倒数第二次的颜色就是2。
对于点3在第二次和第三次染色的时候分别被染色为2, 3,倒数第二次的颜色就是2。

 

 

解题思路:从前往后考虑比较难,不妨从后往前考虑。每次染色我们用树链剖分+线段树来染色。然后再额外的新建一个线段树,用来记录当前节点是第几次染色。那么我们每次染色后,再区间+1来表示这个区间又被染色一次。然后我们每次染色后就可以询问有多少个点被染色了K次,这些点都可以更新答案了,然后再单点查询颜色,单点更新最值变为负无穷,防止下次又被查询到。

 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
typedef unsigned long long ll;
const int MAXN=400005;

struct Edge
{
    int u,v,next;
}e[MAXN];
int head[MAXN];
int edge_num;

void insert_edge(int u,int v){
    e[edge_num].u=u;
    e[edge_num].v=v;
    e[edge_num].next=head[u];
    head[u]=edge_num++;
}

/****树链剖分部分****/
int top[MAXN];
int fa[MAXN];
int deep[MAXN];
int size[MAXN];
int pos[MAXN];//dfs序
int rpos[MAXN];//dfs序对应的原坐标
int son[MAXN];
int SEG;
int N;

//求出fa,deep,num,son
void dfs1(int u,int pre,int d){
    deep[u]=d;
    fa[u]=pre;
    size[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=pre){
            dfs1(v,u,d+1);
            size[u]+=size[v];
            if(son[u]==-1||size[v]>size[son[u]])
                son[u]=v;
        }
    }
}

//求出top和pos,求的过程中,先求重链上的dfs序
void dfs2(int u,int sp){
    top[u]=sp;
    pos[u]=SEG++;
    rpos[SEG-1]=u;
    if(son[u]!=-1)
        dfs2(son[u],sp);
    else
        return;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=son[u]&&v!=fa[u]){
            dfs2(v,v);
        }
    }
}
void init(){
    edge_num=0;
    memset(head,-1,sizeof(head));
    SEG=1;
    memset(son,-1,sizeof(son));
}
/****树链剖分部分结束****/

/****区间最值部分****/
int tree[MAXN<<2];
int lazyAdd[MAXN<<2];

void pushup(int rt){
    tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}

void pushdownAdd(int rt){
    if(lazyAdd[rt]){
        tree[rt<<1]+=lazyAdd[rt];
        lazyAdd[rt<<1]+=lazyAdd[rt];
        tree[rt<<1|1]+=lazyAdd[rt];
        lazyAdd[rt<<1|1]+=lazyAdd[rt];
        lazyAdd[rt]=0;
    }
}

void updateAdd(int L,int R,int C,int l,int r,int rt){
    if(L<=l&&r<=R){
        lazyAdd[rt]+=C;
        tree[rt]+=C;
        return;
    }
    int m=(l+r)/2;
    pushdownAdd(rt);
    if(L<=m)
        updateAdd(L,R,C,l,m,rt<<1);
    if(R>m)
        updateAdd(L,R,C,m+1,r,rt<<1|1);
    pushup(rt);
}

int queryMax(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R)
        return tree[rt];
    int m=(l+r)/2;
    pushdownAdd(rt);
    int ans=-1;
    if(L<=m)
        ans=max(ans,queryMax(L,R,l,m,rt<<1));
    if(R>m)
        ans=max(ans,queryMax(L,R,m+1,r,rt<<1|1));
    return ans;
}
/****区间最值部分结束****/


/****区间染色部分****/
int Col[MAXN<<2];
int lazyCol[MAXN<<2];

void pushdownCol(int rt){
    if(lazyCol[rt]){
        Col[rt<<1]=lazyCol[rt];
        lazyCol[rt<<1]=lazyCol[rt];
        
        Col[rt<<1|1]=lazyCol[rt];
        lazyCol[rt<<1|1]=lazyCol[rt];
        
        lazyCol[rt]=0;
    }
    
}

void updateCol(int L,int R,int C,int l,int r,int rt){
    if(L<=l&&r<=R){
        lazyCol[rt]=C;
        Col[rt]=C;
        return;
    }
    int m=(l+r)/2;
    pushdownCol(rt);
    
    if(L<=m)
        updateCol(L,R,C,l,m,rt<<1);
    if(R>m)
        updateCol(L,R,C,m+1,r,rt<<1|1);
}

int queryCol(int L,int l,int r,int rt){
    if(l==r){
        return Col[rt];
    }
    int m=(l+r)/2;
    pushdownCol(rt);
    if(L<=m)
        return queryCol(L,l,m,rt<<1);
    else
        return queryCol(L,m+1,r,rt<<1|1);
}
/****区间染色部分结束****/

//区间查询等于K的点
vector li;
void queryAns(int L,int R,int K,int l,int r,int rt){
    if(l==r){
        li.push_back(l);
        return;
    }
    int m=(l+r)/2;
    pushdownAdd(rt);
    if(L<=m)
        if(tree[rt<<1]==K)//证明左边有答案
            queryAns(L,R,K,l,m,rt<<1);
    if(R>m)
        if(tree[rt<<1|1]==K)//证明右边有答案
            queryAns(L,R,K,m+1,r,rt<<1|1);
}

/****熟练剖分结合线段树部分****/
void updateAdd(int u,int v,int C){
    int f1=top[u];
    int f2=top[v];
    while(f1!=f2){
        if(deep[f1]deep[v])
        swap(u,v);
    updateAdd(pos[u],pos[v],C,1,N,1);
}

void updateCol(int u,int v,int C){
    int f1=top[u];
    int f2=top[v];
    while(f1!=f2){
        if(deep[f1]deep[v])
        swap(u,v);
    updateCol(pos[u],pos[v],C,1,N,1);
}

int queryMax(int u,int v){
    int f1=top[u];
    int f2=top[v];
    int temp=-1;
    while(f1!=f2){
        if(deep[f1]deep[v])
        swap(u,v);
    return max(temp,queryMax(pos[u],pos[v],1,N,1));
}


void queryPos(int u,int v,int K){
    int f1=top[u];
    int f2=top[v];
    li.clear();
    while(f1!=f2){
        if(deep[f1]deep[v])
        swap(u,v);
    queryAns(pos[u],pos[v],K,1,N,1);
}


int ans[MAXN];
struct node{
    int u,v,c;
}Q[MAXN];

int main(){
    int M,K;
    scanf("%d%d%d",&N,&M,&K);
    int u,v;
    init();
    for(int i=0;i=1;i--){
        updateCol(Q[i].u,Q[i].v,Q[i].c);//区间染色
        updateAdd(Q[i].u,Q[i].v,1);//区间+1
        
        queryPos(Q[i].u,Q[i].v,K);//查询有多少个点被染色了K次,把这些点放在li里
        for(int i=0;i

 

 

 

你可能感兴趣的:(ACM,-,线段树,————ACM相关————,——数据结构——,ACM,-,树链剖分)