P7603 [THUPC2021] 鬼街 题解

P7603 [THUPC2021] 鬼街 题解

第一次见折半报警器的 trick,记录一下

首先观察到 xn105,所以 x 最多有 6 个质因数,x=30030 可以取到,这使得对于修改,我们可以暴力单点修改。

接下来考虑询问,朴素的做法是:每一次灵异事件之后,都对所有监控器进行检验是否满足和为 y

这样复杂度不对,但是我们可以注意到并不是所有时刻都需要对某些监控器进行检验。

这里有一个显然但是关键的结论:

如果 1ikai=y,则 aiyk,这里面的 ai 表示从监控设置开始到监控报警时刻,i 号房的闹鬼次数。

反证法易证。

这启示我们,只在满足上必要条件的情况下,对于一个监控进行检验,检验之后更新限制为:1ikΔai=y1ika1i,其中 Δai 表示从这次检验时间开始,i 号房的闹鬼次数,a1i 表示从上次检验/设置开始,到当前检验时间 i 号房的闹鬼次数。

由于 a1iyk,所以 1ika1iyk 每进行一次检验,都会使得 yyyk,相当于每次 y 会变为原来的 k1k 倍左右,所以最多进行 O(logkk1y) 次检验。

那么我们只需要在每个位置上设置一个 "报警器",在单点设置监控/检验之后,为这个点添加 "报警器" (yk,i),表示如果这个位置的闹鬼次数超过了 yk 就需要检验 i 号监控。

然后每次修改一个点的时候就对这个点上最可能报警的 "报警器" 进行检查,即限制最小的报警器。

这些报警器使用 std::set 或者 __gnu_pbds::tree 可以维护,但是常数略大,因为每次只需要取出最小值,可以使用一个堆来维护。

本题中询问 m 与鬼屋个数 n 同阶,时间复杂度:O(nlogkk1Vlognω(n))

参考代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <ctime>
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/assoc_container.hpp>
//#define int long long
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<ll, int> PII;
const int N = 1e5 + 10;

int n, m, primes[N], tx[N], tot;
ll val[N], sback[N], a[N];
bool st[N];
void sieve() {
    for(int i = 2; i < N; i ++) {
        if(!st[i]) primes[++ tot] = i;
        for(int j = 1; j <= tot && primes[j] * i < N; j ++) {
            st[i * primes[j]] = 1;
            if(i % primes[j] == 0) break;
        }
    }
}
__gnu_pbds::priority_queue<PII, greater<PII>, __gnu_pbds::pairing_heap_tag> warn[N];
vector<__gnu_pbds::priority_queue<PII, greater<PII>, __gnu_pbds::pairing_heap_tag>::point_iterator> back[N];
vector<int> que, pr[N];
void check(int i) {
    ll s = 0, x = tx[i], y = val[i];
    for(auto d : pr[x]) s += a[d];
    y -= s - sback[i], val[i] = y;
    for(int id = 0, d; id < pr[x].size(); id ++) d = pr[x][id], warn[d].erase(back[i][id]);
    if(y <= 0) {
        que.push_back(i);
        return ;
    }
    ll lm = (y + (int)pr[x].size() - 1) / (int)pr[x].size();
    sback[i] = s;
    for(int id = 0, d; id < pr[x].size(); id ++) {
        d = pr[x][id];
        back[i][id] = warn[d].push({lm + a[d], i});
    }
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m, sieve();
    clock_t stt =clock();
    for(int i = 1; i <= tot; i ++)
        for(int p = primes[i], j = p; j <= n; j += p)
            pr[j].push_back(p);
    int cnt = 0;
    for(ll i = 1, op, x, y, lst = 0; i <= m; i ++) {
        cin >> op >> x >> y, y ^= lst;
        if(op == 1) {
            cnt ++;
            tx[cnt] = x, val[cnt] = y;
            ll lm = (y + (int)pr[x].size() - 1) / (int)pr[x].size();
            if(y == 0) que.push_back(cnt);
            else {
                back[cnt].resize(pr[x].size(), 0);
                sback[cnt] = 0;
                for(int id = 0, d; id < pr[x].size(); id ++) {
                    d = pr[x][id];
                    sback[cnt] += a[d];
                    back[cnt][id] = warn[d].push({lm + a[d], cnt});
                }
            }
        }
        else {
            for(auto d : pr[x]) a[d] += y;
            for(auto d : pr[x]) {
                while(warn[d].size() && (warn[d].top().x <= a[d])) 
                    check(warn[d].top().y);
            }
            sort(que.begin(), que.end());
            cout << que.size() << ' ';
            for(auto x : que) cout << x << ' '; cout << '\n';
            lst = que.size();
            que.clear();
        }
    }
    return 0;
}

posted @   MoyouSayuki  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
:name :name
点击右上角即可分享
微信分享提示