例题二 Stone Games (45届ICPC昆明站M题)
-
解题思路
知识点:
主席树,思维
解题过程: 1.首先设 x n = s 1 + s 2 + ⋯ + s n − 1 + s n x_n = s_1 + s_2 + \cdots + s_{n-1} + s_n xn=s1+s2+⋯+sn−1+sn我们假设前 s n s_n sn项可以组成 ( 1 ⋯ x n ) (1\cdots x_n) (1⋯xn)中的任何数。
2.我们可以发现只有当 s n + 1 s_{n+1} sn+1小于等于 x n + 1 x_n + 1 xn+1时,前 s n + 1 s_{n + 1} sn+1项才可以组成 ( 1 ⋯ x n + 1 ) (1\cdots x_{n + 1}) (1⋯xn+1)中的任何数。例如 1 , 2 , 4 , q 1,2,4,q 1,2,4,q只有 q q q大于 8 8 8时才能符合题意,
3.由此我们可以每次求出前 s n + 1 s_{n + 1} sn+1的和 s u m sum sum,如果 s n + 1 s_{n + 1} sn+1 大于 x n x_n xn则说明不能找到直接退出,否则令 x n x_n xn等于 s u m sum sum继续循环。
4.因为每次都是区间操作,并且要有序加入。因此我们可以使用主席树来维护。
-
代码
#include <vector> #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1e6 + 10; typedef long long LL; vector <int> ls; struct nn { LL l, r; LL sum; }tr[N * 40]; LL a[N], root[N], idx; LL n, m; LL build(LL l, LL r) { LL q = ++ idx; if (l == r) return q; LL mid = l + r >> 1; tr[q].l = build(l, mid); tr[q].r = build(mid + 1, r); return q; } void insert(LL p, LL &q, LL l, LL r, LL x) { q = ++idx; tr[q] = tr[p]; tr[q].sum += ls[x - 1]; if (l == r) return; LL mid = l + r >> 1; if (x <= mid) insert(tr[p].l, tr[q].l, l, mid, x); else insert(tr[p].r, tr[q].r, mid + 1, r, x); } LL query(LL p, LL q, LL l, LL r, LL x) { if (x >= r) return tr[q].sum - tr[p].sum; LL ans = 0; if (l == r) return (tr[q].sum - tr[p].sum); LL mid = l + r >> 1; if (l <= x) ans += query(tr[p].l, tr[q].l, l, mid, x); if (mid + 1 <= x) ans += query(tr[p].r, tr[q].r, mid + 1, r, x); return ans; } LL find(LL x) { return lower_bound(ls.begin(), ls.end(), x) - ls.begin() + 1; } int main (){ scanf("%lld%lld", &n, &m); for (LL i = 1; i <= n; i ++) { scanf("%lld", &a[i]); ls.push_back(a[i]); } sort(ls.begin(), ls.end()); ls.erase(unique(ls.begin(), ls.end()), ls.end()); LL nmax= ls.size(); root[0] = build(1, nmax); for (LL i = 1; i <= n; i ++) { insert(root[i - 1], root[i], 1, nmax, find(a[i])); } LL ans = 0; while (m --) { LL l, r; scanf("%lld%lld", &l, &r); l = (l + ans) % n + 1; r = (r + ans) % n + 1; // cout << l << ' ' << r << endl; if (l > r) swap(l, r); LL x = 0; while (1) { LL p = upper_bound(ls.begin(), ls.end(), x + 1) - ls.begin(); if (ls[nmax - 1] <= x) p = nmax; if (p == 0) break; LL k = query(root[l - 1], root[r], 1, nmax, p); // printf("p:%d k:%d ls[p]:%d\n", p, k, ls[p - 1]); if (k == x) break; else x = k; } printf("%lld\n", x + 1); ans = x + 1; } return 0; }