【改题】变量观测
变量观测
这题确实绝妙,考场想了很久,没想出来
题意
\(n\)个变量,\(q\)个操作:\(1\)操作加入一个观测者,给出其观测的\(k\)个变量(\(k\leq3\)),和整数\(t\),当其观测的\(k\)个数变化值之和大于\(t\)时,停止观测。 \(2\)操作给出正整数\(i, x\), 将第\(i\)个变量加上\(x\)。
题解
题解是这么说的:把每个人的\(t\)分成\(k\)份均摊到\(k\)个变量(每个\(t/k\)), 假如每个变量的变化值都小于\(t/k\),那么总的变化值显然不到\(t\)。如果其中一个大于等于\(t/k\),那就把放进去的\(k\)个取出来,检查这个是否已经观察完了,如果没有就把还需要的变化值重新放回去,这时候需要的变化值至少是原来的\(\frac 23\),每个观察者重复次数不超过\(\log_{1.5} t\), 复杂度正确。
过于炫酷,以至于很难理解为什么能够想到:首先我们容易想到的是,需要用堆来维护,比如说k=1的情况下,你给每一个变量开一个堆, 这样你可以从需要变化量最小的开始check。但是如果\(k\)大于\(1\), 那么你每次修改的时候就麻烦了,因为\(k=1\)你可以给整个堆加某个数,随便处理一下,但是\(k\neq1\)的时候就要同时修改不同堆的某些元素,无法实现。
以下内容纯粹口胡
问题在于,你每次修改一个堆里的元素,还要同时修改其他元素,所以要想到能否把元素拆开下放到这几个堆里,独立去做,这里是可以想到的。 另一种理解方式是:这里实际上是一种延迟触发机制,由于\(t\)很大,我们每次check显然不优,需要一定延迟机制,但同时要保证答案正确。
实现
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <map>
#define int long long
using namespace std;
int read(){
int num=0, flag=1; char c=getchar();
while(!isdigit(c) && c!='-') c=getchar();
if(c == '-') flag=-1, c=getchar();
while(isdigit(c)) num=num*10+c-'0', c=getchar();
return num*flag;
}
struct point{
int id, v, qid;
bool operator< (const point& x) const{
return v > x.v;
}
}lp;
const int N = 3e5+600;
int n, q, lst=0, cnt=0, qcnt=0;
int a[N], query[N][5];
map<int, int> del;
priority_queue<point> s[N];
vector<int> ans;
int getsum(int id){
int sum = 0;
for(int i=1; i<=query[id][0]; i++) sum += a[query[id][1+i]];
return sum;
}
void push(int id){
int sum = getsum(id);
int x = query[id][1];
int v = (x-sum)/query[id][0];
v = max(v, 1ll);
qcnt++;
lp.id = id;
lp.qid = qcnt;
for(int i=1; i<=query[id][0]; i++){
lp.v = v + a[query[id][i+1]];
s[query[id][i+1]].push(lp);
}
}
int check(int id){
int sum = getsum(id);
if(sum >= query[id][1]) {
return 1;
}
return 0;
}
void clear(int id){
while(!s[id].empty() && del[s[id].top().qid])
s[id].pop();
}
signed main(){
freopen("obs.in", "r", stdin);
freopen("obs.out", "w", stdout);
n=read(), q=read();
// for(int i=1; i<=n; i++) a[i] = read();
while(q--){
int type = read();
if(type == 1){
int t=read()^lst, k=read(); cnt++;
query[cnt][0] = k, query[cnt][1] = t;
for(int i=1; i<=k; i++){
query[cnt][i+1] = read()^lst;
query[cnt][1] += a[query[cnt][i+1]];
}
push(cnt);
}else{
int t=read()^lst, v=read()^lst;
a[t] += v;
clear(t);
ans.clear();
while(!s[t].empty() && s[t].top().v <= a[t]){
del[s[t].top().qid] = 1;
if(check(s[t].top().id)){
ans.push_back(s[t].top().id);
}else{
push(s[t].top().id);
}
s[t].pop();
clear(t);
}
printf("%lld", lst = ans.size());
sort(ans.begin(), ans.end());
for(auto x : ans){
printf(" %lld", x);
}printf("\n");
}
}
return 0;
}