Codeforces 983E NN country 倍增 树状数组

2 篇文章 0 订阅
1 篇文章 0 订阅

http://codeforces.com/contest/983/problem/E

 

E. NN country

time limit per test

3 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

In the NN country, there are ?n cities, numbered from 11 to ?n, and ?−1n−1 roads, connecting them. There is a roads path between any two cities.

There are ?m bidirectional bus routes between cities. Buses drive between two cities taking the shortest path with stops in every city they drive through. Travelling by bus, you can travel from any stop on the route to any other. You can travel between cities only by bus.

You are interested in ?q questions: is it possible to get from one city to another and what is the minimum number of buses you need to use for it?

Input

The first line contains a single integer ?n (2≤?≤2⋅1052≤n≤2⋅105) — the number of cities.

The second line contains ?−1n−1 integers ?2,?3,…,??p2,p3,…,pn (1≤??<?1≤pi<i), where ??pi means that cities ??pi and ?i are connected by road.

The third line contains a single integer ?m (1≤?≤2⋅1051≤m≤2⋅105) — the number of bus routes.

Each of the next ?m lines contains 22 integers ?a and ?b (1≤?,?≤?1≤a,b≤n, ?≠?a≠b), meaning that there is a bus route between cities ?a and ?b. It is possible that there is more than one route between two cities.

The next line contains a single integer ?q (1≤?≤2⋅1051≤q≤2⋅105) — the number of questions you are interested in.

Each of the next ?q lines contains 22 integers ?v and ?u (1≤?,?≤?1≤v,u≤n, ?≠?v≠u), meaning that you are interested if it is possible to get from city ?v to city ?u and what is the minimum number of buses you need to use for it.

Output

Print the answer for each question on a separate line. If there is no way to get from one city to another, print −1−1. Otherwise print the minimum number of buses you have to use.

Examples

input

Copy

7
1 1 1 4 5 6
4
4 2
5 4
1 3
6 7
6
4 5
3 5
7 2
4 5
3 2
5 3

output

Copy

1
3
-1
1
2
3

input

Copy

7
1 1 2 3 4 1
4
4 7
3 5
7 6
7 6
6
4 6
3 1
3 2
2 7
6 3
5 3

output

Copy

1
-1
-1
1
-1
1

Note

Routes for first sample are marked on the picture.

 

参考:

https://www.cnblogs.com/zsnuo/p/9057523.html

http://www.cnblogs.com/PotremZ/p/9416225.html

 

题意:

给定一棵 nn 个点的树, mm 条链, qq 个询问,每次询问 aa 到 bb 之间的路径最少可用几条给定链完全覆盖,无解输出 −1−1 。

分析:

对于每一条 aa 与bb 间的路径,都可拆分为 aa 到 lcalca 的路径和 bb 到 lcalca 的路径

采用贪心策略, low[x][i]low[x][i] 表示从 xx 点出发向上选择不超过 2i2i 条链可抵达的深度最浅的点。这时对于每一个询问可将询问的两个端点修改为利用贪心策略跳到的深度大于 lcalca 且深度最小的节点,并记录下答案,这个过程可以用倍增完成。注意特判端点即 lcalca 的情况。

然后出现两种情况。若修改后的两个端点出现在同一条给定链上,答案为原答案 +1+1 ,否则答案为原答案 +2+2 。问题模型转换为,每次询问一个点对是否出现在同一条给定链上。记录下 dfsdfs 序,在深搜过程中利用树状数组统计即可。

时间复杂度 O(nlogn)O(nlogn) 。

 英文题解:

http://codeforces.com/blog/entry/59484

Let's say we go down when we go towards the root, and we go up when we go against the root. Also we say that vertex ?a lower than vertex ?b if ?a is closer to the root.

Each way from ?v to ?u can be represented as two parts: at first we go down from ?v to ???lca and then we go up from ???lca to ?u.

Let's say we have to use ?a buses to go from ?v to ???lca and ?b buses to go from ???lca to ?u. Let's learn how to calculate ?a and ?b fast. Firstly, notice that we can move down greedily. So we calculate ??????[?]lowest[v] as the lowest vertex, where we can go from ?v using only one bus. Secondly, notice that we can now build binary lifting on ??????lowest.

The answer is either ?+?a+b or ?+?−1a+b−1. Let's say the lowest vertex we can go from ?v using ?−1a−1 buses is ??lv and for ?u and ?−1b−1buses it's ??lu. ??lv and ??lu can be also calculated using binary lifting on ??????lowest. Then, if there is a route connecting ??lv and ??lu, the answer is ?+?−1a+b−1 and it's ?+?a+b otherwise.

Let's calculate ??lv and ??lu for all the queries.

Now we build an euler's tour. For each we now know the interval, corresponding to it's subtree. Let's say it is [????_??[?],????_???[?])[time_in[v],time_out[v]). Then we run dfs again and do the following: when we came into ??lv we ask for sum on [????_??[??],????_???[??])[time_in[lu],time_out[lu]). Now for each way, starting in ??lv we add 11 to its other end. Now we run dfs for all of its children. And now we ask for sum on [????_??[??],????_???[??])[time_in[lu],time_out[lu]) for the second time. If it changed the route connecting ??lv and ??lu exists.

Asymptotics is ?(?⋅???(?)+?⋅???(?)+?⋅???(?))O(n⋅log(n)+m⋅log(n)+q⋅log(n)).

Read the solution for better understanding. It tried to make it as readable as possible.

我的理解:

先对所有城市构成的树做个倍增求LCA,

然后把low求出来,

这时两个城市如果有路线的话,无非两种情况

(近表示(该点深度) - (LCA深度)最小)  

1. 从城市x出发,坐车到离x城和y城的LCA最近的点,坐1次车经过LCA到z城,再坐车从z城到y城。

2. 从城市x出发,坐车到离x城和y城的LCA最近的点,坐1次车到LCA,再坐车从LCA到z城,最后坐车从z城到y城。

3. x城本身就是LCA,或y城本身就是LCA。

low数组的作用就是让我们可以快速求出x城和y城各自离LCA最近的点。但是毕竟倍增不能求出在LCA拐弯后反向路径,所以得按上述分类讨论。

#include<bits/stdc++.h>
using namespace std;

int n, m, Q, cntEdge, dfsOrder, val;

const int maxn = 200050;
const int inf = 400000;

struct Edge{
	int to, nxt;
}edges[maxn*2];

struct Query{
	int x, y, lca;
}q[maxn];

int fa[maxn][20], low[maxn][20], in[maxn], out[maxn], head[maxn], 
		deep[maxn], tr[maxn], last[maxn], ok[maxn], ans[maxn];

vector<int> bus[maxn];
vector<int> queryAt[maxn];

void insertEdge(int fr, int to){
	edges[cntEdge].to = to;
	edges[cntEdge].nxt = head[fr];
	head[fr] = cntEdge++;
}

void dfs(int cur){
	//printf("dfs %d\n", cur);
	in[cur] = ++dfsOrder;
	for (int i=1; (1<<i)<=deep[cur]; i++){
		fa[cur][i] = fa[fa[cur][i-1]][i-1];
	}

	for (int i=head[cur]; i!=-1; i=edges[i].nxt){
		int to = edges[i].to;
		deep[to] = deep[cur] + 1;
		dfs(to);
	}

	out[cur] = dfsOrder;
}

int getLca(int x, int y){
	if (deep[x] < deep[y]) swap(x, y);
	int diff = deep[x] - deep[y];
	for (int i=17; i>=0; i--){
		if ((1<<i) <= diff){
			diff -= (1<<i);
			x = fa[x][i];
		}
	}

	if (x == y) return x;

	for (int i=17; i>=0; i--){
		if ((1<<i)<deep[x] && fa[x][i] != fa[y][i]){
			x = fa[x][i]; y = fa[y][i];
		}
	}
	//printf("lca %d %d = %d\n", x, y, fa[x][0]);
	return fa[x][0];
}


void dfslow(int cur){

	for (int i=head[cur]; i!=-1; i=edges[i].nxt){
		int to = edges[i].to;
		dfslow(to);
		if (deep[low[to][0]] < deep[low[cur][0]]){
			low[cur][0] = low[to][0];
		}
	}

}

int find(int x, int lca){
	if (deep[low[x][17]] > deep[lca]){
		val = -inf;
		return -1;
	}
	val = 0;
	if (x == lca) { val = -1; return 0;}
	for (int i=17; i>=0; i--){
		//printf("low[%d][%d] = %d\n", x, i, low[x][i]);
		//printf("deep[low[%d][%d]] = %d\n", x, i, deep[low[x][i]]);
		if (deep[low[x][i]] > deep[lca]){
			x = low[x][i]; val |= (1<<i);
		}
	}
	//printf("find %d %d = %d\n", x, lca, val);
	return x;
}

inline int lowbit(int x){return x&(-x);}

void add(int x, int v){for (;x<=n;x+=lowbit(x)) tr[x]+=v;}

int query(int x){int ans = 0; for (;x;x-=lowbit(x)) ans+=tr[x]; return ans;}

void work(int x){
	for (auto qnum : queryAt[x]){
		last[qnum] = query(out[q[qnum].y]) - query(in[q[qnum].y] - 1);
	}

	for (auto des : bus[x]){
		add(in[des], 1);
		//if (x == 7) printf("add[%d]\n", des);
	}

	for (int i=head[x]; i!=-1; i=edges[i].nxt){
		work(edges[i].to);
	}

	for (auto qnum : queryAt[x]){
		int now = query(out[q[qnum].y]) - query(in[q[qnum].y] - 1);
		if (q[qnum].x == 2 && q[qnum].y == 7){
			//printf("last = %d now = %d\n", last[qnum], now);
		}
		if (now != last[qnum]) ok[qnum] = 1;
	}
}
int main(){
	memset(head, -1, sizeof(head));
	scanf("%d", &n);
	for (int i=2; i<=n; i++){
		scanf("%d", &fa[i][0]);
		insertEdge(fa[i][0], i);
	}

	dfs(1);
	scanf("%d", &m);

	for (int i=1; i<=n; i++){
		low[i][0] = i;
	}

	for (int i=0; i<m; i++){
		int x, y, lca;
		scanf("%d%d", &x, &y);
		lca = getLca(x, y);
		if (deep[lca] < deep[low[x][0]])
			low[x][0] = lca;
		if (deep[lca] < deep[low[y][0]])
			low[y][0] = lca;
		bus[x].push_back(y);
		bus[y].push_back(x);
	}

	dfslow(1);
	for (int t=1; t<=n; t++){
		for (int i=1; i<=17; i++){
			low[t][i] = low[low[t][i-1]][i-1];
		}
	}

	scanf("%d", &Q);
	for (int i=0; i<Q; i++){
		scanf("%d%d", &q[i].x, &q[i].y);
		q[i].lca = getLca(q[i].x, q[i].y);
		ans[i] = 2;
		int x = find(q[i].x, q[i].lca); ans[i]+=val;
		int y = find(q[i].y, q[i].lca); ans[i]+=val;
		//printf("x = %d y = %d\n", x, y);
		if (x>0 && y>0){
			q[i].x = x; q[i].y = y;
			queryAt[x].push_back(i);
		}
	}
	work(1);
	for (int i=0; i<Q; i++)
		if (ok[i]) ans[i]--;

	for (int i=0; i<Q; i++)
		printf("%d\n", ans[i]<0?-1:ans[i]);

	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值