luogu P5068 [Ynoi2015]我回来了 题解

一道水题,真正意义上我的第一道手切 Ynoi。

题意(已翻译):有一个01序列,初始全为0。每次修改操作是把一个位置改成1。另外定义 \(f(x)\) 为把该序列每 \(x\) 个分一块,每个块内都至少有一个 1 的最长前缀块数。查询操作是,对于每对 \([l, r]\) 求出 \(\sum_{x=l}^r{f(x)+1}\)\((1\le n\le1\times10^5,1\le m\le 1\times 10^6)\)

考虑暴力移动指针的次数为 \(\sum_{x=1}^n\frac{1}{x}=O(n\log n)\) 可以接受,所以就考虑如何维护指针的移动。

由于是单点修改,每次修改只会影响到所有的分的块中的一个,因此需要一种数据结构来维护之。每个修改的块是一个区间,可以把他们拍到线段树上。这样每次修改会影响的区间就是根节点到叶子节点上的所有涉及的区间,然后暴力改就彳亍了。最后题目求的是区间和,套个树状数组好了。

这样每次修改是 \(O(\log n)\) 的,这一部分是 \(O(m\log n)\) 的,我们有 \(O(n\log n)\) 个区间,拍到线段树是 \(O(n\log n^2)\)。所以整体复杂度是 \(O(m\log n+n\log n^2)\)。记得每次修改到线段树上的一个节点就要将上面记录的区间清空,否则复杂度就假了。(实测 1 s -> 4 min 40 s(大雾))。

看代码能理解的快一些。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, m, t[maxn], d[maxn], vs[maxn];
bitset<maxn>q[11]; bitset<maxn/10>q1[maxn]; //卡空间用的,就当是计一个区间的状态的
vector<pair<int,int>>tg[maxn << 2];
inline void ins(int p, int x){while(p<=n) t[p]+=x, p+=p&-p;}
inline int qry(int p){int r=0; while(p) r+=t[p], p-=p&-p; return r;}//树状数组
inline void upd(int p, int l, int r, int L, int R, pair<int,int>x){//区间拍到线段树上
    if(l >= L && r <= R) return void(tg[p].emplace_back(x));
    int mid = l + r >> 1; if(L <= mid) upd(p * 2, l, mid, L, R, x);
    if(R > mid) upd(p * 2 + 1, mid + 1, r, L, R, x);
}
inline void mdf(int p, int l, int r, int k){ //修改操作
    for(auto o:tg[p]){ auto &[x, id] = o; int ls=d[x];
        if(x <= 10) {q[x][id]=1; while(q[x][d[x]])d[x]++;}
        else {q1[x][id]=1; while(q1[x][d[x]]) d[x]++;}
        if(d[x]-ls) ins(x, d[x]-ls); //暴力移动指针+树状数组修改
    } tg[p].clear(); if(l == r) return; //记的清空标记
    int mid = l + r >> 1;
    if(k <= mid) mdf(p * 2, l, mid, k);
    else mdf(p * 2 + 1, mid + 1, r, k);
}
int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);std::cout.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; i++) ins(i, 1), d[i] = 1; //d数组是指针
    for(int i = 1; i <= n; i++) for(int j = 1; j <= (n - 1) / i + 1; j++)
        upd(1, 1, n, (j - 1) * i + 1, min(n, i * j), std::make_pair(i, j));
    int op, l, r; while(m--){cin >> op >> l;
        if(op == 1) {if(!vs[l]) mdf(1, 1, n, l), vs[l]|=1;}
        else cin >> r, cout << qry(r) - qry(l - 1) << '\n';}
}
posted @ 2023-10-27 17:35  xlpg0713  阅读(25)  评论(1)    收藏  举报