1.题目描述:点击打开链接
2.解题思路:本题是典型的树链剖分题目,不过本题的一个难点在于如何维护每个结点处数量最多的谷物型号。树链剖分经常和离线标记法结合使用,本题亦然。可以用一个vector来维护区间端点处增加的谷物种类。addv[L].push_back(z),subv[R+1].push_back(z)。通过树链剖分可以快速标记好每个区间。接下来就是扫描一遍每个结点,并用一个线段树来维护每个区间上数量最多的那个谷物型号。这样问题即可解决。详细过程请参考代码。
3.代码:
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<complex>
#include<functional>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <ll, ll> P;
#define lid (id<<1)
#define rid (id<<1|1)
const int N=100000+20;
const int maxo=N*4;
struct Edge
{
int to;
int next;
}edge[N*2];
int head[N],top[N],fa[N],son[N],depth[N],num[N];
int p[N],fp[N];
int tot,pos;
vector<int>addv[N];
vector<int>subv[N];
void init()
{
tot=0;pos=1;
memset(head,-1,sizeof(head));
memset(son,-1,sizeof(son));
}
void addedge(int u,int v)
{
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs1(int u,int pre,int d)
{
depth[u]=d;
fa[u]=pre;
num[u]=1;
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].to;
if(v!=pre)
{
dfs1(v,u,d+1);
num[u]+=num[v];
if(son[u]==-1||num[v]>num[son[u]])
son[u]=v;
}
}
}
void dfs2(int u,int sp)
{
top[u]=sp;
p[u]=pos++;
fp[p[u]]=u;
if(son[u]==-1)return;
dfs2(son[u],sp);
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].to;
if(v!=son[u]&&v!=fa[u])
dfs2(v,v);
}
}
void distribute(int u,int v,int z)//给区间[u,v]上都增加一个种类为v的谷物
{
while(top[u]!=top[v])
{
if(depth[top[u]]<depth[top[v]])swap(u,v);
addv[p[top[u]]].push_back(z);
subv[p[u]+1].push_back(z);
u=fa[top[u]];
}
if(depth[u]>depth[v])swap(u,v);
addv[p[u]].push_back(z);
subv[p[v]+1].push_back(z);
}
int maxv[maxo],maxvID[maxo];//maxv[i]表示结点为i的区间最大的谷物数量,maxvID[i]表示结点为i的区间最大谷物数量对应的谷物型号
void pushup(int id)
{
if(maxv[lid]>=maxv[rid])
{
maxv[id]=maxv[lid];
maxvID[id]=maxvID[lid];
}
else
{
maxv[id]=maxv[rid];
maxvID[id]=maxvID[rid];
}
}
int qx,qv;//qx是当前的谷物型号,qv是增加量
void update(int id,int L,int R)
{
if(L==qx&&qx==R)
{
maxv[id]+=qv;
maxvID[id]=qx;
return;
}
int M=L+(R-L)/2;
if(qx<=M)update(lid,L,M);
else update(rid,M+1,R);
pushup(id);
}
int ans[N];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)&&(n||m))
{
init();
for(int i=0;i<n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs1(1,1,1);
dfs2(1,1);
for(int i=1;i<=pos;i++)
addv[i].clear(),subv[i].clear();
int mz=0; //mz是最大的谷物型号,则线段树的整个区间就是[1,mz]
for(int i=0;i<m;i++)
{
int u,v,z;
scanf("%d%d%d",&u,&v,&z);
mz=max(mz,z);
distribute(u,v,z);
}
me(maxv);
for(int i=1;i<pos;i++)
{
for(int j=0;j<addv[i].size();j++)//计算左端点
{
qx=addv[i][j],qv=1;
update(1,1,mz);
}
for(int j=0;j<subv[i].size();j++)//计算右端点,最终的maxv[1]就是当前结点i处最多的谷物数量,maxvID[1]对应的就是型号
{
qx=subv[i][j],qv=-1;
update(1,1,mz);
}
if(!maxv[1])ans[fp[i]]=0;
else ans[fp[i]]=maxvID[1];
}
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}
}