【YBT2022寒假Day1 A】变量观测(模拟)(分治)

变量观测

题目链接: 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;
}
posted @ 2022-02-06 01:18  あおいSakura  阅读(39)  评论(0编辑  收藏  举报