题意:给出一个树。我们有以下操作:在节点u,v的路径上,对所有节点加入一个给定种类的货物。最后,对每个节点输出该点数目最多的货物的数量。
思路:看到树上的路径,是不是该想到树链剖分呢?但是因为货物的种类太多了,没法状态压缩。 但是,因为相当于是离线查询,我们是可以离线搞搞的。
首先利用树链剖分,利用差分的思想,打上标记。这样,我们就可以从前往后根据重标号,从前往后递推就可以了。
然后,我们需要一个东西来维护两个关键字,即:先比较货物的数量,在货物数量相同的时候,比较货物的编号。用set就可以了。
代码如下:
#pragma comment(linker,"/STACK:102400000,102400000")
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
using namespace std;
const int MAX = 100100;
struct event{
int po,va,ty;
event(int p,int v,int t):po(p),va(v),ty(t){}
bool operator < (const event & rhs) const{
if(po != rhs.po) return po < rhs.po;
else return ty > rhs.ty;
}
};
struct point{
int x, y;
point(int xx, int yy):x(xx),y(yy){}
bool operator < (const point & rhs) const{
if(x != rhs.x) return x > rhs.x;
else return y < rhs.y;
}
};
vector<event> Event;
set<point> S;
int cnt[MAX];
int ans[MAX];
int N,M;
int x,y,z;
int head[MAX],nxt[MAX<<2],to[MAX<<2],tot;
int fa[MAX];//father of node
int top[MAX];//top node of every heavy chain
int deep[MAX];// deepth of every node
int num[MAX];// the number of node's children
int p[MAX];//new index of each node
int fp[MAX];//the node number of new index
int son[MAX];//heavy son of each node
int pos;// count of new index
void init()
{
Event.clear();
S.clear();
tot = pos = 0;
memset(head,-1,sizeof(head));
memset(son,-1,sizeof(son));
memset(cnt,0,sizeof(cnt));
}
void addedge(int u, int v)
{
to[tot] = v; nxt[tot] = head[u];
head[u] = tot++;
to[tot] = u; nxt[tot] = head[v];
head[v] = tot++;
}
void dfs1(int u,int pre,int d)
{
deep[u] = d;fa[u] = pre;num[u] = 1;
for(int i = head[u];i != -1; i = nxt[i])
{
int v = to[i];
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){
dfs2(son[u],sp);
for(int i = head[u]; i != -1; i = nxt[i]){
int v = to[i];
if(v != son[u] && v != fa[u])
dfs2(v,v);
}
}
}
//update of node of value
//the index of node is the
void update(int u, int v,int z)
{
int f1 = top[u], f2 = top[v];
while(f1 != f2){
if(deep[f1] < deep[f2]){
swap(f1,f2);
swap(u,v);
}
Event.push_back(event(p[f1],z,1));
Event.push_back(event(p[u]+1,z,-1));
u = fa[f1]; f1 = top[u];
}
if(deep[u] > deep[v]) swap(u,v);
Event.push_back(event(p[u],z,1));
Event.push_back(event(p[v]+1,z,-1));
}
void erase(int ty)
{
set<point>::iterator it = S.find(point(cnt[ty],ty));
if(it != S.end()) S.erase(it);
if(cnt[ty]) cnt[ty]--;
if(cnt[ty]) S.insert(point(cnt[ty],ty));
}
void insert(int ty)
{
set<point>::iterator it = S.find(point(cnt[ty],ty));
if(it != S.end()) S.erase(it);
cnt[ty]++;
if(cnt[ty]) S.insert(point(cnt[ty],ty));
}
int main(void)
{
//freopen("input.txt","r",stdin);
while(scanf("%d %d", &N, &M), N||M){
init();
for(int i = 0; i < N - 1; ++i){
scanf("%d %d", &x,&y);
addedge(x,y);
}
dfs1(1,0,0);
dfs2(1,1);
for(int i = 0; i < M; ++i){
scanf("%d %d %d", &x,&y,&z);
update(x,y,z);
}
sort(Event.begin(),Event.end());
for(int i = 0, sz = Event.size(),j = 0; i < pos; ++i){
while(j < sz && Event[j].po <= i){
if(Event[j].ty == -1) erase(Event[j].va);
else insert(Event[j].va);
++j;
}
if(S.empty()) ans[i] = 0;
else ans[i] = S.begin()->y;
}
for(int i = 1; i <= N; ++i)
printf("%d\n",ans[p[i]]);
}
return 0;
}
第二种思路:
我们可以不偷懒,在树链剖分的前提下,用线段树来求解。
开始的步骤一样,我们首先对剖分后的区间打标记。
然后,我们建立线段树。在这道题中的线段树是保存的:在当前树上的节点下,各个种类货物的数量。对于每个线段树的节点,保存的是,对应货物编号区间的数量的最大值。
然后,对于原来树上的节点,我们从前向后递推,把对应的标记加入线段树中。查询最大的数量和对应的编号,我们从上往下尽可能的,向下向左走(最小的编号),同时保证该区间的最大值为整个区间上的最大值。
代码可见:http://blog.csdn.net/u013368721/article/details/39449273