Description
有 n 个区间,第 i 个区间为 [li,ri]。保证 l1<l2<⋯<ln 且 r1<r2<⋯<rn。其中一部分区间是特殊的,输入会给定。
如果第 i 个区间和第 j 个区间相交,那么 i,j 之间有一条边。保证 1,n 联通。
给定 Q 组询问,每次给定 a,b 满足 1≤a<b≤n,你需要回答 a 到 b 至少要经过多少条边,以及有多少个特殊区间对应的点,使得这个点可能在 a 到 b 的最短路径上。
n,Q≤2×105。
Solution
显然一个区间肯定是尽量跳能跳的最远的区间,那么设 nxti 表示区间 i 能跳的最远的区间,第一问就可以直接倍增求了。
第二问实际上是求:
i=a∑bai×[dis(a,i)+dis(i,b)=dis(a,b)]
设 dis(a,b)=len,钦定 dis(a,i)=x,dis(i,b)=len−x,还是不好做。
容易发现 ∀i∈[a,b],dis(a,i)+dis(i,b)≥dis(a,b),所以只要满足 dis(a,i)≤x,dis(i,b)≤len−x 即可。
如果设 Li,j 表示 i 往左跳 2j 步到达的点,Ri,j 表示 i 往右跳 2j 步到达的点,那么满足条件的 i 要满足 i∈[Lb,len−x,Ra,x],答案就是:i=1∑len−1cnt(Lb,len−x,Ra,x)
考虑设 sumi 表示前 i 个区间的特殊区间的个数,sli,j=k=i−2j∑i−1sumk−1,sri,j=k=i+1∑i+2jsumk。
预处理后只要在询问里面倍增即可。
时间复杂度:O((n+q)logn)。
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>
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; while (T--) dickdreamer(); return 0; }
|