【树上差分】【离线操作】TTL与村庄

【题目描述】
有一个村庄在闹饥荒,善良而土豪的 TTL 决定给他们发放救济粮,该村庄有 n 户人家, 每两户人家之间只有一条路可以互相到达,即这些人家之间形成一棵树。现在 TTL 会以这 样的形式给他们发放粮食,选择两户人家,然后对这两个户人家路径上的所有人家都发放一 袋种类为 w 的救济粮。在完成一系列发放任务后, TTL 想知道每一户人家收到的粮食中数 量最多的是哪一种。

【输入】
第一行两个数 n, q,其中 n 表示村庄共有几户人家, q 表示 TTL 一共发放了几次粮食

接下来 n-1 行,每行两个数 x y,表示编号为 x 和 y 的两户人家之间连有边

接下来 q 行,每行三个数 x y w,表示 TTL 选择了 x 到 y 的路径,对每户人家发放 1 袋种类为 w 的粮食。

【输出】
输出 n 行,第 i 行输出编号为 i 的人家收到的粮食中数量最多的种类号,如果有多个数 量相同的粮食,输出其中最小的种类号,如果没有收到粮食,输出 0
【样例输入 】
2 4
1 2
1 1 1
1 2 2
2 2 2
2 2 1
【样例2输入】:
5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3
【样例输出】
1
2
【样例2输出】:
2
3
3
0
2

【思路】
首先呢,这道题正解显然是树链剖分。但是我太菜了,想不出正解,只能用些歪门邪道骗分,竟然还AC了。
我们可以对每一种路径修改的w进行排序,对每一种w分类处理。利用树上差分进行路径修改,每种颜色结束后深搜处理每个人家的粮食,并尝试更新答案。最后再卡卡常,输出即可。
代码:(也许用欧拉序求lca会快一点,但是这道题这个做法的的瓶颈在于w的数量,不在于lca,但是卡卡常倒不错

#include
#include
#include
#include
#include
#include
#include
#define re register
using namespace std;
int n,m,a,b,s,t;
const int N=2e5+1;
int f[N];
int nxp[N<<2|1];
int ans[N];
int id[N];
int cnt=0,tot=0;
struct ndeo{
	int u,v;
}e[N];
inline void add(int &u,int &v)
{
	e[++cnt].u=u;
	e[cnt].v=v;
	nxp[cnt]=f[u];
	f[u]=cnt;
}
int fa[N][20];
int dep[N];
int lo[21]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072};
void dfs(int u)
{
	for(int re i=1;lo[i]<=dep[u];i++)
		fa[u][i]=fa[fa[u][i-1]][i-1];
	for(int re i=f[u];i;i=nxp[i])
	{
		int v=e[i].v;
		if(!dep[v])
		{
			dep[v]=dep[u]+1;
			fa[v][0]=u;
			dfs(v);
		}
	}
}
inline int lca(int a,int b)
{
	if(dep[a]<dep[b])swap(a,b);
	int t=dep[a]-dep[b];
	for(int re i=0;lo[i]<=t;i++)
		if(t&lo[i])a=fa[a][i];
	if(a==b)return a;
	for(int re i=17;i>=0;i--)
	{
		if(fa[a][i]!=fa[b][i])
		{
			a=fa[a][i];
			b=fa[b][i];
		}
	}
	return fa[a][0];
}
char p;
int now=0;
int sum[N];
int cf[N];
struct no{
	int w;
	int x,y;
}q[N];
inline bool cmp(no a,no b){return a.w<b.w;}
inline int red()
{
    int data=0;char ch=0;
    ch=getchar();
    while((ch<'0' || ch>'9')) ch=getchar();//假读入优化,这里应该判负,但是为了卡常,下面输出优化亦然。
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data;
}
int Dfs(int u)
{
	for(int re i=f[u];i;i=nxp[i])
	{
		int v=e[i].v;
		if(dep[v]==dep[u]+1)
		{
			cf[u]+=Dfs(v);
		}
	}
	if(cf[u]>ans[u])
	{
		ans[u]=cf[u];
		id[u]=now;
	}
	return cf[u];
}
void print(int x)
{
    if(x>9)
        print(x / 10);
    putchar(x % 10 + '0');
    return;
}
int main()
{
	n=red();m=red();
	for(int re i=1;i<n;i++)
	{
		a=red();b=red();
		add(a,b);add(b,a);
	}
	dep[1]=1;
	dfs(1);
	for(int re i=1;i<=m;i++)
	{
		q[i].x=red();
		q[i].y=red();
		q[i].w=red();
	}
	sort(q+1,q+m+1,cmp);
	now=q[1].w;
	for(int re i=1;i<=m;i++)
	{
		if(now!=q[i].w)
		{
			Dfs(1);
			memset(cf,0,sizeof(cf));
			now=q[i].w;
		}
		a=q[i].x,b=q[i].y;
		int lc=lca(a,b);
		cf[a]++;
		cf[b]++;
		cf[lc]--;
		if(lc!=1)
		cf[fa[lc][0]]--;
	}
	Dfs(1);
	for(int re i=1;i<=n;i++)
		print(id[i]),putchar('\n');
}



你可能感兴趣的:(【树上差分】【离线操作】TTL与村庄)