SPOJ--Count on a tree II(树上莫队)

Description

 

You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight.

We will ask you to perfrom the following operation:

  • u v : ask for how many different integers that represent the weight of nodes there are on the path from u to v.

 

Input

In the first line there are two integers N and M.(N<=40000,M<=100000)

In the second line there are N integers.The ith integer denotes the weight of the ith node.

In the next N-1 lines,each line contains two integers u v,which describes an edge (u,v).

In the next M lines,each line contains two integers u v,which means an operation asking for how many different integers that represent the weight of nodes there are on the path from u to v.

Output

For each operation,print its result.

Example

Input:
   
   
8 5
8 2 105 2 9 3 8 5 7 7 1 2 1 3 1 4 3 5 3 6 3 7 4 8 2 5 7 8
Output: 4
4 

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <string>
#include <set>
#include <stack>
using namespace std;
#define LL long long int
#define maxn 100800
#define maxm 210008
int vv[maxm],nxt[maxm],first[maxn],e,deep;
void init()
{
	memset(first,-1,sizeof(first));
	e = deep = 0;
}
void addedge(int u,int v)
{
	vv[e] = v;	nxt[e] = first[u];	first[u] = e++;
	vv[e] = u;	nxt[e] = first[v];	first[v] = e++;
}
int val[maxn],in[maxn],out[maxn];
int col[maxn],a[maxn],dep[maxn];
int lisan[maxm],cnt;
int fa[maxn][20];
int ID(int x)
{
	return lower_bound(lisan,lisan+cnt,x) - lisan;
}

void dfs(int u,int pre)
{
	in[u] = ++deep;	a[deep] = val[u];
	col[deep] = 1;	dep[u] = dep[pre] + 1;
	for(int i = 0;fa[u][i];i++)
		fa[u][i+1] = fa[fa[u][i]][i];
	for(int i = first[u];i != -1;i = nxt[i])
	{
		int v = vv[i];
		if(v != pre)	
			fa[v][0] = u,dfs(v,u);
	}
	out[u] = ++deep;	a[deep] = val[u];
	col[deep] = -1;
}
int jump(int u,int d)
{
	for(int i = 0;d;i++)	if(d >> i & 1)
		u = fa[u][i],d -= 1 << i;
	return u;
}
int lca(int u,int v)
{
	if(dep[u] < dep[v])	swap(u,v);
	u = jump(u,dep[u] - dep[v]);
	for(int i = 19;i >= 0;i--)
		if(fa[u][i] != fa[v][i])
			u = fa[u][i],v = fa[v][i];
	return u == v ? u:fa[u][0];
}

int B;
struct Q
{
	int u,v;
	int b,id;
	void scan(int i)
	{
		id = i;
		scanf("%d%d",&u,&v);
		if(in[u] > in[v])	swap(u,v);
		b = in[u] / B;
	}
	bool operator < (const Q & a) const
	{
		return b < a.b || a.b == b && in[v] < in[a.v];
	}
}q[maxm];
int F,U,V;
int ans,num[maxn];
int sum[maxn];
void gao(int x,int k)
{
	int pre = num[x];
	num[x] += k;
	if(pre == 0 && num[x] == 1) ++ans;
	if(pre == 1 && num[x] == 0) --ans;
}
int solve( int f, int u, int v )
{
	while( F < f ){
		++F;
		gao( a[F], -col[F] );
		gao( a[F], -col[F] );
	}
	while( F  > f ){
		gao( a[F], col[F] );
		gao( a[F], col[F] );
		--F;
	}
	gao( a[f], col[f] );

	while( U < u ){
		++U;
		gao( a[U], col[U] );
	}
	while( U > u ){
		gao( a[U], -col[U] );
		--U;
	}
	while( V < v ){
		++V;
		gao( a[V], col[V] );
	}
	while( V > v ){
		gao( a[V], -col[V] );
		--V;
	}
	int tmp = ans;
	gao( a[f], -col[f] );
	return tmp;
}
int main()
{
	int n,m,u,v;
	scanf("%d%d",&n,&m);
	cnt = 0;
	init();
	for(int i = 1;i <= n;i++)
	{
		scanf("%d",&val[i]);
		lisan[cnt++] = val[i];
	}
	sort(lisan,lisan+cnt);
	cnt = unique(lisan,lisan+cnt) - lisan;
	for(int i = 1;i <= n;i++)	val[i] = ID(val[i]);
	memset(first,-1,sizeof(first));
	e = 0;
	for(int i = 1;i < n;i++)
	{
		scanf("%d%d",&u,&v);
		addedge(u,v);
	}
	B = sqrt(n+0.5);
	memset(fa,0,sizeof(fa));
	dfs(1,0);
	for(int i = 0;i < m;i++)
		q[i].scan(i);
	sort(q,q+m);
	memset( num, 0, sizeof(num) );
	F = U = V = ans = 0;
	int f;
	for( int i = 0; i < m; ++i ){
		u = q[i].u, v = q[i].v;
		f = lca( u, v );
		sum[q[i].id] = solve( in[f], in[u], in[v] );
	}
	for( int i = 0; i < m; ++i )
		printf( "%d\n", sum[i] );
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值