[PKUSC2018]星际穿越 倍增DP

版权声明:xgc原创文章,未经允许不得转载。 https://blog.csdn.net/xgc_woker/article/details/82794205

Description
n个点的图,点i和[l[i],i)的所有点连双向边。每次询问(l,r,x)表示x到[l,r]的所有点的最短路径长度和。


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


Sample Output
3/2
13/5
3/2
2/1
1/1


这篇blog写的不错,可以了解一下。
然后在BZOJ会T,就把LL删了,竟然过了。。。


#include <cstdio>
#include <cstring>

using namespace std;
int _min(int x, int y) {return x < y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
typedef long long LL;

int f[20][310000], L[310000];
int s[20][310000];

int gcd(int a, int b) {
	if(b == 0) return a;
	return gcd(b, a % b);
}

inline int calc(int l, int r) {
	if(L[r] <= l) return r - l;
	int ans = r - L[r], tot = 1; r = L[r];
	for(int j = 18; j >= 0; j--) {
		if(f[j][r] > l) ans += s[j][r] + (r - f[j][r]) * tot, r = f[j][r], tot += 1 << j;
	} ans += (r - l) * (tot + 1);
	return ans;
}

int main() {
	int n = read();
	for(int i = 2; i <= n; i++) L[i] = read();
	s[0][n] = n - L[n]; f[0][n] = L[n];
	for(int i = n - 1; i >= 2; i--) f[0][i] = _min(f[0][i + 1], L[i]), s[0][i] = i - f[0][i];
	for(int i = 1; i <= 18; ++i) {
		for(int j = (1 << i); j <= n; ++j) {
			f[i][j] = f[i - 1][f[i - 1][j]];
			s[i][j] = s[i - 1][j] + s[i - 1][f[i - 1][j]] + (f[i - 1][j] - f[i][j]) * (1LL << i - 1);
		}
	} int m = read();
	for(int i = 1; i <= m; ++i) {
		int l = read(), r = read(), x = read();
		int ans = calc(l, x) - calc(r + 1, x);
		int uu = r - l + 1, d = gcd(ans, uu);
		printf("%d/%d\n", ans / d, uu / d);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xgc_woker/article/details/82794205
今日推荐