【改题】变量观测


变量观测

这题确实妙,考场想了很久,没想出来


题意

\(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\)的时候就要同时修改不同堆的某些元素,无法实现。

问题在于,你每次修改一个堆里的元素,还要同时修改其他元素,

------------恢复内容开始------------

# 变量观测

这题确实妙,考场想了很久,没想出来


题意

\(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 
#include 
#include 
#include 
#include 
#include 
#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 del;
priority_queue s[N];
vector 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;
}