变量观测
题目链接: YBT2022寒假Day1 A
题目大意
给你 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;
}