P9019 [USACO23JAN] Tractor Paths P 题解

Description

nn 个区间,第 ii 个区间为 [li,ri][l_i,r_i]。保证 l1<l2<<lnl_1<l_2<\cdots<l_nr1<r2<<rnr_1<r_2<\cdots<r_n。其中一部分区间是特殊的,输入会给定。

如果第 ii 个区间和第 jj 个区间相交,那么 i,ji,j 之间有一条边。保证 1,n1,n 联通。

给定 QQ 组询问,每次给定 a,ba,b 满足 1a<bn1\le a < b\le n,你需要回答 aabb 至少要经过多少条边,以及有多少个特殊区间对应的点,使得这个点可能在 aabb 的最短路径上。

n,Q2×105n,Q\le 2\times 10^5

Solution

显然一个区间肯定是尽量跳能跳的最远的区间,那么设 nxtinxt_{i} 表示区间 ii 能跳的最远的区间,第一问就可以直接倍增求了。

第二问实际上是求:

i=abai×[dis(a,i)+dis(i,b)=dis(a,b)]\sum_{i=a}^{b}{a_i\times [dis(a,i)+dis(i,b)=dis(a,b)]}

dis(a,b)=lendis(a,b)=len,钦定 dis(a,i)=x,dis(i,b)=lenxdis(a,i)=x,dis(i,b)=len-x,还是不好做。

容易发现 i[a,b],dis(a,i)+dis(i,b)dis(a,b)\forall i\in[a,b],dis(a,i)+dis(i,b)\geq dis(a,b),所以只要满足 dis(a,i)x,dis(i,b)lenxdis(a,i)\leq x,dis(i,b)\leq len-x 即可。

如果设 Li,jL_{i,j} 表示 ii 往左跳 2j2^j 步到达的点,Ri,jR_{i,j} 表示 ii 往右跳 2j2^j 步到达的点,那么满足条件的 ii 要满足 i[Lb,lenx,Ra,x]i\in[L_{b,len-x},R_{a,x}],答案就是:i=1len1cnt(Lb,lenx,Ra,x)\sum\limits_{i=1}^{len-1}{cnt(L_{b,len-x},R_{a,x})}

考虑设 sumisum_i 表示前 ii 个区间的特殊区间的个数,sli,j=k=i2ji1sumk1,sri,j=k=i+1i+2jsumksl_{i,j}=\sum\limits_{k=i-2^j}^{i-1}{sum_{k-1}},sr_{i,j}=\sum\limits_{k=i+1}^{i+2^j}{sum_k}

预处理后只要在询问里面倍增即可。

时间复杂度:O((n+q)logn)O((n+q)\log n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5;

int n, q;
int a[kMaxN], l[kMaxN], r[kMaxN], sum[kMaxN];
int nxt[kMaxN][20], pre[kMaxN][20], sumn[kMaxN][20], sump[kMaxN][20];

int getdis(int s, int t) {
if (s == t) return 0;
int ret = 1;
for (int i = 19; ~i; --i)
if (nxt[s][i] < t)
s = nxt[s][i], ret += (1 << i);
return ret;
}

void dickdreamer() {
std::string str1, str2;
std::cin >> n >> q >> str1 >> str2;
int ccl = 0, ccr = 0;
for (int i = 1; i <= 2 * n; ++i) {
if (str1[i - 1] == 'L') {
l[++ccl] = i;
} else {
r[++ccr] = i;
}
}
for (int i = 1; i <= n; ++i) {
a[i] = (str2[i - 1] == '1');
sum[i] = sum[i - 1] + a[i];
}
for (int i = 1; i <= n; ++i) {
int L = i, R = n + 1, res = i;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (l[mid] <= r[i]) L = res = mid;
else R = mid;
}
nxt[i][0] = res;
sumn[i][0] = sum[res];
L = 0, R = i, res = i;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (r[mid] >= l[i]) R = res = mid;
else L = mid;
}
pre[i][0] = res;
sump[i][0] = sum[res - 1];
}
for (int i = 1; i <= 19; ++i) {
for (int j = 1; j <= n; ++j) {
nxt[j][i] = nxt[nxt[j][i - 1]][i - 1];
pre[j][i] = pre[pre[j][i - 1]][i - 1];
sumn[j][i] = sumn[j][i - 1] + sumn[nxt[j][i - 1]][i - 1];
sump[j][i] = sump[j][i - 1] + sump[pre[j][i - 1]][i - 1];
}
}
for (int cs = 1; cs <= q; ++cs) {
int s, t;
std::cin >> s >> t;
int mi = getdis(s, t), cnt = a[s] + a[t];
for (int i = 19; ~i; --i)
if ((mi - 1) >> i & 1)
cnt -= sump[t][i], t = pre[t][i];
for (int i = 19; ~i; --i)
if ((mi - 1) >> i & 1)
cnt += sumn[s][i], s = nxt[s][i];
std::cout << mi << ' ' << cnt << '\n';
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P9019 [USACO23JAN] Tractor Paths P 题解
https://sobaliuziao.github.io/2023/10/04/post/3d06fc6.html
作者
Egg_laying_master
发布于
2023年10月4日
许可协议