链接: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 <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <algorithm>
#include<queue>
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<int> 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[f2]){
swap(f1,f2);
swap(u,v);
}
updateAdd(pos[f1],pos[u],C,1,N,1);
u=fa[f1];
f1=top[u];
}
if(deep[u]>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[f2]){
swap(f1,f2);
swap(u,v);
}
updateCol(pos[f1],pos[u],C,1,N,1);
u=fa[f1];
f1=top[u];
}
if(deep[u]>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[f2]){
swap(f1,f2);
swap(u,v);
}
temp=max(temp,queryMax(pos[f1],pos[u],1,N,1));
u=fa[f1];
f1=top[u];
}
if(deep[u]>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[f2]){
swap(f1,f2);
swap(u,v);
}
queryAns(pos[f1],pos[u],K,1,N,1);
u=fa[f1];
f1=top[u];
}
if(deep[u]>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<N-1;i++){
scanf("%d%d",&u,&v);
insert_edge(u,v);
insert_edge(v,u);
}
dfs1(1,0,0);
dfs2(1,1);
for(int i=1;i<=M;i++)
scanf("%d%d%d",&Q[i].u,&Q[i].v,&Q[i].c);
for(int i=M;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<li.size();i++)//更新答案并置为负无穷
{
ans[rpos[li[i]]]=queryCol(li[i],1,N,1);//注意区分好dfs序和原序
updateAdd(rpos[li[i]],rpos[li[i]],-0x3f3f3f3f);
}
}
for(int i=1;i<=N;i++)
cout<<ans[i]<<" ";
return 0;
}