hdu 4916 Count on the path

Count on the path

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 378    Accepted Submission(s): 113


Problem Description
bobo has a tree, whose vertices are conveniently labeled by 1,2,…,n.

Let f(a,b) be the minimum of vertices  not on the path between vertices a and b.

There are q queries (u i,v i) for the value of f(u i,v i). Help bobo answer them.
 

Input
The input consists of several tests. For each tests:

The first line contains 2 integers n,q (4≤n≤10 6,1≤q≤10 6). Each of the following (n - 1) lines contain 2 integers a i,b i denoting an edge between vertices a i and b i(1≤a i,b i≤n). Each of the following q lines contains 2 integer u′ i,v′ i (1≤u i,v i≤n).

The queries are encrypted in the following manner.

u 1=u′ 1,v 1=v′ 1.
For i≥2, u i=u′ i⊕f(u i - 1,v i - 1),v i=v′ i⊕f(u i-1,v i-1).

Note ⊕ denotes bitwise exclusive-or.

It is guaranteed that f(a,b) is defined for all a,b.

The task contains huge inputs. `scanf` in g++ is considered too slow to get accepted. You may (1) submit the solution in c++; or (2) use hand-written input utilities. 
 

Output
For each tests:

For each queries, a single number denotes the value.
 

Sample Input
  
  
4 1 1 2 1 3 1 4 2 3 5 2 1 2 1 3 2 4 2 5 1 2 7 6
 

Sample Output
  
  
4 3 1
 

Author
Xiaoxu Guo (ftiasch)
 

Source
 

题意:给你一棵树,最多有1e6个点。不断询问a->b外的最小点。

基本仿照gxx的标程来写的,感觉学到了很多。这题主要是考一些树上的细腻的操作。

思路见注释

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <functional>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <cstdlib>
#include <ctime>
#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
#define INF 1e9
#define MAXN 21
const int maxn = 1000005;
#define mod 1000000007
#define eps 1e-7
#define pi 3.1415926535897932384626433
#define rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define scan(n) scanf("%d",&n)
#define scanll(n) scanf("%I64d",&n)
#define scan2(n,m) scanf("%d%d",&n,&m)
#define scans(s) scanf("%s",s);
#define ini(a) memset(a,0,sizeof(a))
#define out(n) printf("%d\n",n)
//ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b);}
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int q[maxn];
int path[maxn]; //path[i]记录表示在i所在的子树中(以1为根),除i到1这条路径外的最小值
int child[maxn][4]; //child[i][0]表示以i为根且不包括i的树的最小点,child[i][1]为次小,child[i][2]为第三小
int fa[maxn]; 
int belong[maxn]; //belong[i]表示i所在的子树(以1为根)的最小值,可用来快速判断两点是否位于同一子树
int subtree[maxn];//subtree[i]表示以i为根(包括i)的子树的最小值
vector<int> G[maxn]; //邻接表
int n,m;
int next_int()
{
	int result = 0;
	char c = getchar();
	while (!isdigit(c)) {
		c = getchar();
	}
	while (isdigit(c)) {
		result = result * 10 + c - '0';
		c = getchar();
	}
	return result;
}
int query(int a,int b)
{

	if(a > b) swap(a,b);
	if(a != 1 && (belong[a] == belong[b])) return 1; //若两点位于同一子树,则不经过1
	int i = 0;
	while(child[1][i] == belong[a] || child[1][i] == belong[b]) i++; //找出除a,b所在子树外最小的点

	int ans = a == 1 ? path[b] : min(path[a], path[b]); //a,b所在子树最小的点(a->1,b->1的路径除外)
	ans = min(ans, child[1][i]);
	return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	//    freopen("out.txt","w",stdout);
#endif  
	while(~scanf("%d%d",&n,&m))
	{
		int a,b;
		rep(i,n+1) G[i].clear();
		rep(i,n-1)
		{
			a = next_int();
			b = next_int();
			G[a].push_back(b); //用了vector保存邻接表,不断pb g++T了,用C++过了
			G[b].push_back(a);
		}

		int head = 0, tail = 0;
		q[tail++] = 1;
		fa[1] = -1;
		while(head < tail)
		{
			int u = q[head++];
			for(int i = 0;i <(int)G[u].size(); i++)
			{
				int v = G[u][i];
				if(v == fa[u]) continue;
				fa[v] = u;
				q[tail++] = v;
			}
		}
		rep1(i,n) rep(j,4) child[i][j] = INF;
		for(int i = tail-1; i >= 0; i--)
		{
			int v = q[i];
			subtree[v] = min(child[v][0], v);
			int u = fa[v];
			if(u != -1)
			{
				child[u][3] = subtree[v];
				sort(child[u], child[u] + 4);
				//path[v] = min(child[u][0]
			}
		}

		head = tail = 0;
		for(int i = 0;i < (int)G[1].size(); i++)
		{
			int u = G[1][i];
			belong[u] = subtree[u];
			path[u] = INF;
			q[tail++] = u;
		}
		path[1] = INF;
		belong[1] = -1;
		while(head < tail)
		{
			int u = q[head++];
			for(int i = 0;i < (int)G[u].size(); i++)
			{
				int v = G[u][i];
				if(v == fa[u]) continue;
				belong[v] = belong[u];
				path[v] = min(path[u], child[u][subtree[v] == child[u][0]]);
				q[tail++] = v;
			}
			path[u] = min(path[u], child[u][0]);
		}
		int last = 0;
		while(m--)
		{
			a = next_int();
			b = next_int();
			a ^= last;
			b ^= last;
			int ans = query(a,b);
			last = ans;
			printf("%d\n",ans);
		}
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值