给你 n 个数,要你在线维护两种操作:
给一个数加一个值,或者设立一个观察者观察一些数,从当时开始观察,当观察的数的变化值的和大于一个设定值是结束观察。
然后对于每个加数的操作你要输出有多少个观察者结束观察。
发现一个人监视的顶多三个人,我们考虑用这么一个神奇的方法。
(因为如果只能监视一个人我们可以直接用 set 模拟)
我们考虑把每个人的阀值设为 \(\left\lceil \dfrac{v}{k}\right\rceil\)(\(v\) 是权值,\(k\) 是监视的人数)
这有什么用呢,如果所有人都没有到达这个值,那就一定不会达到 \(v\)。
那有一个到了之后,我们就可以从 set 中拿出来试一下。
如果可以就行,不可以的话,我们发现我们可以分剩下的数。
那每次至少减少 \(2/3\) 的量,那就是一个 \(\log_{1.5}v\) 的次数。
所以是可以的。
#include<set> #include<cstdio> #define ll long long using namespace std; ll n, q, lst, x, y, z, a[200001], e[200001]; ll dy[200001][4], p, c[200001][4]; set <pair<ll, ll> > lim[200001]; set <ll> ans; void check(ll x) { ll tmp = e[x]; for (ll i = 1; i <= dy[x][0]; i++) { ll y = dy[x][i]; tmp -= a[y]; lim[y].erase(make_pair(c[x][i], x)); } if (tmp <= 0) { ans.insert(x); } else { for (ll i = 1; i <= dy[x][0]; i++) { ll y = dy[x][i]; c[x][i] = a[y] + (tmp + dy[x][0] - 1) / dy[x][0]; lim[y].insert(make_pair(c[x][i], x)); } } } int main() { // freopen("obs.in", "r", stdin); // freopen("obs.out", "w", stdout); scanf("%lld %lld", &n, &q); while (q--) { scanf("%lld %lld %lld", &x, &y, &z); if (x == 1) { y ^= lst; dy[++p][0] = z; e[p] = y; for (ll i = 1; i <= dy[p][0]; i++) { scanf("%lld", &z); z ^= lst; e[p] += a[z]; dy[p][i] = z; c[p][i] = (y + dy[p][0] - 1) / dy[p][0]; lim[z].insert(make_pair(c[p][i], p)); } } else { ans.clear(); y ^= lst; z ^= lst; a[y] += z; while (lim[y].size() && a[y] >= (*lim[y].begin()).first) { check((*lim[y].begin()).second); } lst = ans.size(); printf("%lld", lst); for (set <ll> ::iterator it = ans.begin(); it != ans.end(); it++) printf(" %lld", *it); printf("\n"); } } return 0; }