暑期集训第二周

第二周全部是数据结构。

8.8 树套树

这些题都很毒瘤,比卡时间更恶心的是卡空间,对我这种人傻常数大的选手很不友好。

P3380 【模板】树套树

P12685 [国家集训队] 排队 加强版

很久以前写的,原题过了,加强版被卡常了,待补。外层树状数组在下标维上开,内层动态开点线段树在值域维上开。

CF575I Robots protection

将对三角形的操作转化到矩阵上。方法在暑期集训第一周中介绍过了。

以上是简单题


P6749 『MdOI R3』Yoshino

同样外层树状数组在下标维上开,内层动态开点线段树在值域维上开。运用颜色段均摊的思想,我们可以把一个等差数列 \(a[l,\dots,r]\) 看作 \(l\) 这个位置上有这个数列的全部数,而 \(a[l+1,\dots,r]\) 是空的。这样我们很方便的完成添加等差数列的操作:只用修改位置 \(l\) 就可以。我们还可以将一个等差数列分裂成两段,即变成 \(l\) 中存有 \(a[l,\dots,mid-1]\),\(mid\) 存有 \(a[mid,r]\)。通过分裂操作,就可以完整的处理修改操作:若要将 \([l,r]\) 修改为等差数列,先通过分裂操作,将原本横跨 \(l,r\) 点的等差数列劈成两端。接着把所有在 \([l,r]\) 内的等差删掉,最后把新的数列放上来就可以。逆序对数量统计在这个过程中逐步算就可以。参考题解。

内层值域线段树的标记永久化:如果修改是区间,而询问是单点的,可以使用标记永久化。在这道题使用标记永久化可以不用 pushdown,减少新增节点数量,节约空间。最开始没有标记永久化被卡了。

P3688 [ZJOI2017] 树状数组

前面概率的部分想清楚后维护树套树相对简单一点。又用到标记永久化。只要想清楚操作的先后顺序不影响答案就能理解这样做的正确性。不用应该也可以,不过没写过,不清楚是否能过。

P1903 【模板】带修莫队 / [国家集训队] 数颜色 / 维护队列

与 P1972 [SDOI2009] HH 的项链 相比多了修改操作,这使得我们不能离线下来然后扫描线做。换成树套树就可以简单做到在线的查询。修改操作的话拿 set 维护一下 \(pre\),想清楚 \(pre\) 改变了哪些就可以。

P5445 [APIO2019] 路灯

在集训之前就遇到了这道好题。题目的查询很特别,问的是在此以前有哪些时刻是联通的,我们可以通过贡献提前计算解决。如果两个站点在 \(t\) 时刻联通了,就将它们间的权值加上 \(q-t\),表示接下来他们都将联通。同理如果 \(t\) 时刻断了,就减去 \(q-t\)。在 \(i\) 时刻查询时,如果还是联通状态,记得还要减去 \(q-i\)。因为这里的“权值”指的是从头到尾一共亮了多久,而查询只是从头到 \(i\) 的。

将连续一段亮着的灯称为一个连通块,改变一个路灯的状态相当于改变左右两个连通块间所有点对的状态,这在平面上是一个矩形,是很好维护的。

8.9 扫描线技巧和数据结构优化

扫描线

P8512 [Ynoi Easy Round 2021] TEST_152

由于是覆盖,所以我们只关心一个点最后一次的值,这告诉我们要把询问挂到 \(r\) 上然后做扫描线。如果一个点最后一次操作是 \(i\) 给的,称这个点属于操作 \(i\)。那么当我们处理了 \([1,R]\) 所有操作后,查询 \([L,R]\) 相当于问属于 \([L,R]\) 的所有点的点权和。ODT 和树状数组维护属于 \(i\) 的点的点权和就可以。

P11536 [NOISG 2023 Finals] Curtains

把询问挂到 \(R\) 上,扫描线,逐渐加入 \(r_i\le R\) 的幕布。记 \(f_i\) 表示如果要覆盖 \(i\),幕布的左端点最大能是多少。如果 \(\min\limits_{i=s_j}^{e_j} f_i \ge s_j\) 就可以做到(实际上只可能取等。加入幕布直接将 \([l,r]\) 的 \(f\) 与 \(l\) 取 \(\max\) 就可以。

mex 相关

前两道题是之后 8.11 可持久化线段树中的题目。但明显合理的顺序是先做 8.11 的两道。

P4137 Rmq Problem / mex

这道题告诉我们怎么在线求 mex。值域上建立可持久化线段树,查询的时候拿两颗线段树差分一下,线段树上二分就可以。

CF1436E Complicated Computations

一个区间的 \(\mathrm{mex} = a\) 要求它不含有 \(a\),除此之外,它包含的数字越多越好。所以,存在子区间的 \(\mathrm{mex} = a\),相当于把原数组看作被 \(a\) 划分成的若干段,这些段中有一个区间的 \(\mathrm{mex} = a\)。

P13780 「o.OI R2」愿天堂没有分块

完全是上一题的加强版。一个很重要的结论:极短的 \(\mathrm{mex}\) 至多有 \(2n\) 个。证明在下面可略过。

设 \([l,r]\) 是一个极短 \(\mathrm{mex}\)。

在 \([l+1,r]\) 中 \(0,\dots,a_l-1\) 全部出现,而 \(a_l\) 没有出现。故 \(\operatorname{mex}(a_{l+1},\dots,a_r)=a_l\)

  • 若存在 \(j\in[l+1,r]\) 使 \(a_j=a_l\),则删去位置 \(l\) 后区间内出现的数值集合不变,\(\mathrm{mex}\) 不变,与极短矛盾。
  • 若存在 \(v<a_l\) 不在 \([l+1,r]\) 中出现,由于 \(v\ne a_l\),\(v\) 也不在 \([l,r]\) 中出现,于是 \(M\le v<a_l\),即 \(a_l\) 不参与 \(\mathrm{mex}\) 的判定,删去 \(a_l\) 不改变 \(\mathrm{mex}\),矛盾。

对称地,\(\operatorname{mex}(a_l,\dots,a_{r-1})=a_r\)


显然 \(a_l\ne a_r\),且不妨设 \(a_l>a_r\)。结合 \(\operatorname{mex}(a_l,\dots,a_{r-1})=a_r\) 知

\[\operatorname{mex}(a_{l+1},\dots,a_{r-1})=a_r \]


固定 \(l\),令 \(f(x)=\operatorname{mex}(a_{l+1},\dots,a_x)\ (x\ge l+1)\)。

若 \([l,r]\) 极短且 \(a_l>a_r\),由前两步知 \(f(r-1)=a_r<a_l,f(r)=a_l\ge a_l\)

当 \(r=l+1\) 时 \(f(l)=\operatorname{mex}(\varnothing)=0=a_r<a_l\),同样成立。

由于 \(f\) 单调不降,“使 \(f(x)\ge a_l\) 的 \(x\)” 唯一确定。所以每个左端点 \(l\) 至多对应一个满足 \(a_l>a_r\) 的极短 mex 区间,这类区间至多 \(n\) 个。

把同样的论证从右往左做可得:每个右端点 \(r\) 至多对应一个满足 \(a_l<a_r\) 的极短 mex 区间,这类区间也至多 \(n\) 个。

又由第二步极短 mex 区间必有 \(a_l\ne a_r\),两类已经覆盖全部情形,得证。

还有通过“序列上的区间在平面上的点对刻画”的证明,但是要画图所以算了。


回到这道题上,我们可以预处理出所有的极小 \(\operatorname{mex}\) 区间。先处理出 \(\operatorname{mex}\le 2\) 的区间,然后从 \(2\) 开始扩展,找到其前后第一个等于 \(\operatorname{mex}\) 的数,计算扩展后的区间的 \(\operatorname{mex}\),把新的区间塞到对应 \(\operatorname{mex}\) 的 vector 里。如是扩展 \(\mathrm{mex}=1,\dots,i-1\) 的区间后,对于已经求出来的 \(\operatorname{mex} = i\) 的区间,排序一遍将有包含关系的区间去掉就可以。

然后对于每一个询问 \([L,R]\),我们需要求最小的 \(x\),使得 \([L,R]\) 内部没有 \(\operatorname{mex}=x\) 的极小区间,扫描线加上权值线段树二分就做完了。

具体的:我们把所有极小 \(\operatorname{mex}\) 区间挂到其 \(R\) 上,从小到大对 \(R\) 扫描线,权值线段树维护每个 \(\mathrm{mex}\) 的最大的 \(L\),对于询问线段树二分即可。

非常难受的是在线求 \(\mathrm{mex}\) 要一个可持久化线段树,最后扫描线的时候也要一个权值线段树。按我的写法如果开两棵树的话是会 MLE 的,所以只能共用一个。

P9970 [THUPC 2024 初赛] 套娃

类似的,由上述方法求出极短的 \(\mathrm{mex}\) 区间后,对于每个区间 \([l,r]\) ,设它左边最靠近它的值为 \(\mathrm{mex}\) 的数的下标为 \(L\),类似的右边为 \(R\)。那么它最短的长度是 \(r-l+1\),最长能扩展的是 \(R-L-1\)。

不太好说清楚呢,看代码可能好懂一点。

set<int> s;
vector<int> add[maxn + 5] , del[maxn + 5];
fo(i , 0 , n){
    for(auto x : f[i]){ // mex 为 i 的所有区间存在 vector f[i] 中
        int pre = *(--lower_bound(num[i].begin() , num[i].end() , x.first));
        int nxt = *(upper_bound(num[i].begin() , num[i].end() , x.second));
        pre++ , nxt--;
        add[x.second - x.first + 1].push_back(i);
        del[nxt - pre + 1].push_back(i);
    }
}
fo(i , 0 , n) s.insert(i); // s 是补集
fo(i , 1 , n){
    for(int mex : add[i]){
        if(cnt[mex] == 0) s.erase(mex);
        cnt[mex]++;
    }
    cout << (*s.begin()) << " ";
    for(int mex : del[i]){
        cnt[mex]--;
        if(cnt[mex] == 0) s.insert(mex);
    }
}

序列上的区间在平面上的点对刻画

P14638 [NOIP2025] 序列询问

NOIP 2025 T4,当初在考场上写了一个暴力就跑了。将区间在二维平面上表示,很直观,但是依然还没有补完。

8.10 线段树高级应用和线段树二分

单侧递归

P4198 楼房重建

最基础经典的题。pushup 是 \(O(\log n)\) 的,单次修改要做 \(O(\log n)\) 次 pushup,总复杂度 \(O(\log^2 n)\)。

QOJ8229. 栈

很复杂的一道题……

首先我们会换维。注意到查询的实际上是单点的信息,于是扫描序列轴,用数据结构维护时间轴。然后,然后我写不动了,看这篇题解吧,我觉得写的很好。不过他的代码太不清真了,这里给出我的代码,自认为比较清晰。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<vector>
#define mp make_pair
#define chkmax(a , b) a = max(a , b)
#define chkmin(a , b) a = min(a , b)
#define fo(i , x , y) for(int i = (x) ; i <= (y) ; i++)
#define go(i , x , y) for(int i = (x) ; i >= (y) ; i--)
#define local
#ifdef local
#define debug(x) cerr << #x" : " << x << ", "
#define debugn(x) cerr << #x" : " << x << "\n"
#define debugarr(a , n) cerr << #a" :\n"; fo(i , 1 , n) cerr << a[i] << " "; cerr << "\n"
#define debugs(s) cerr << s
#else
#define debug(x) "n-buna bless me."
#define debugn(x) "Nayutan bless me."
#define debugarr(a , n) "wowaka bless me."
#define debugs(s) "Deco*27 bless me."
#endif
using namespace std;
const int maxn = 100000;
const int maxpoint = maxn * 2;
long long val[maxn + 5];
struct node{
    long long cntl , cntr , sum;
    friend node operator + (const node a , const node b){
        return {a.cntl + b.cntl , a.cntr + b.cntr , a.sum + b.sum};
    }
    friend node operator - (const node a , const node b){
        return {a.cntl - b.cntl , a.cntr - b.cntr , a.sum - b.sum};
    }
};
struct Segment_Tree{
    private:
    node t[maxpoint + 5];
    int tot , ls[maxpoint + 5] , rs[maxpoint + 5];
    node cal(long long del , int l , int r , int k){
        if(l == r){
            if(t[k].cntr <= del) return {t[k].cntl - t[k].cntr , 0 , 0};
            else return {t[k].cntl - del , t[k].cntr - del , (t[k].cntr - del) * val[l]};
        }
        int mid = (l + r) / 2;
        if(t[rs[k]].cntr <= del){
            long long newdel = del + t[rs[k]].cntl - t[rs[k]].cntr;
            return cal(newdel , l , mid , ls[k]) + (node){newdel - del , 0 , 0};
        }
        else{
            return t[k] - t[rs[k]] + cal(del , mid + 1 , r , rs[k]);
        }
    }
    public:
    int build(int l , int r){
        int k = ++tot;
        if(l == r) return k;
        int mid = (l + r) / 2;
        ls[k] = build(l , mid);
        rs[k] = build(mid + 1 , r);
        return k;
    }
    void update(int x , int op , long long num , int l , int r , int k){
        if(l == r){
            if(op == 1){ // update (
                t[k].cntr += num , t[k].sum += val[l] * num;
            }
            else{ // update )
                t[k].cntl += num;
            }
            return;
        }
        int mid = (l + r) / 2;
        if(x <= mid) update(x , op , num , l , mid , ls[k]);
        else update(x , op , num , mid + 1 , r , rs[k]);
        t[k] = cal(t[rs[k]].cntl , l , mid , ls[k]) + t[rs[k]];
    }
    node query(int R , long long del , int l , int r , int k){
        if(r <= R){
            node ans = cal(del , l , r , k);
            ans.cntl += del;
            return ans;
        }
        int mid = (l + r) / 2;
        if(mid + 1 <= R){
            node ansr = query(R , del , mid + 1 , r , rs[k]);
            node ansl = query(R , ansr.cntl , l , mid , ls[k]);
            ansr.cntl = 0;
            return ansl + ansr;
        }
        else return query(R , del , l , mid , ls[k]);
    }
}tr;
int n , m;
struct Opera{
    int op , t;
    long long cnt;
    int f;
};
vector<Opera> q[maxn + 5];
long long res[maxn + 5];
int main(){
    ios :: sync_with_stdio(0) , cin.tie(0) , cout.tie(0);
    // freopen(".in" , "r" , stdin);
    // freopen(".out" , "w" , stdout);
    #ifdef local
    clock_t starttime = clock();
    #endif
    cin >> n >> m;
    fo(i , 1 , m){
        res[i] = -1;
        int op , l , r;
        long long cnt;
        cin >> op;
        if(op == 1){
            cin >> l >> r >> cnt >> val[i];
            q[l].push_back((Opera){1 , i , cnt , 0});
            q[r + 1].push_back((Opera){1 , i , -cnt , 0});
        }
        else if(op == 2){
            cin >> l >> r >> cnt;
            q[l].push_back((Opera){2 , i , cnt , 0});
            q[r + 1].push_back((Opera){2 , i , -cnt , 0});
        }
        else{
            res[i] = 0;
            long long L , R;
            cin >> l >> L >> R;
            q[l].push_back({3 , i , R , 1});
            q[l].push_back({3 , i , L - 1 , -1});
        }
    }
    tr.build(1 , m);
    fo(i , 1 , n){
        for(Opera x : q[i]){
            if(x.op == 1){
                tr.update(x.t , 1 , x.cnt , 1 , m , 1);
            }
            else if(x.op == 2){
                tr.update(x.t , 2 , x.cnt , 1 , m , 1);
            }
            else{
                node ans = tr.query(x.t , 0 , 1 , m , 1);
                long long del = max(0ll , ans.cntr - x.cnt);
                ans = tr.query(x.t , del , 1 , m , 1);
                res[x.t] += x.f * ans.sum;
            }
        }
    }
    fo(i , 1 , m)
        if(res[i] != -1) cout << res[i] << "\n";
    #ifdef local
    clock_t endtime = clock();
    double elapsed = (double)(endtime - starttime) / CLOCKS_PER_SEC;
    debugn(elapsed);
    #endif
}

李超线段树

标记永久化的思想。

CF932F Escape Through Leaf

典题。启发式合并加李超线段树是 \(O(n \log^2 n)\) 的,李超线段树直接合并是 \(O(n \log n)\)。注意到直接在数据结构上合并可以比启发式合并少一个 \(\log\)。另一篇文章省选级的串串入门中的 LOJ6198 也有类似的复杂度分析。

CF1303G Sum of Prefix Sums

典题。点分治加李超线段树,不知道多久以前就做过了。

线段树上二分

CF1479D Odd Mineral Resource

哈希真的想不到,想题的时候甚至想到了 \(O(\frac{n^2}{w})\) bitset 做法。想到哈希之后的可持久化线段树和二分就是水到渠成啦。学到了 mt19937_64 可以生成 unsigned long long 范围内的数。

势能分析

HDU 5306 Gorgeous Sequence

阉割版的吉司机线段树。做法是记录最小值 \(min1\) 与严格次小值 \(min2\)。对于一次取 \(\mathrm{chkmin(x)}\) 操作,如果 \(x \le min1\),不用管。如果 \(min1 < x < min2\),只修改 \(min1\) 的标记。否则暴力修改。

我们通过势能分析,来证明复杂度是 \(O((n+q)\log n)\) 的。定义一个节点的势能是不同元素的多少。上述操作保证了每次暴力递归都会使势能减小 \(1\),即把 \(min1\) 与 \(min2\) 变为相同。总势能是 \(O(n \log n)\) 的,故总复杂度也是这个。

HDU 这个 OJ 一直很神秘,通过 vjudge 交了很多次都是 TLE。一直以为是复杂度假了,然而把关了同步流的 cin cout 换成 scanf printf 就过了。

接下来时简单应用。

UOJ 228 基础数据结构练习题

定义势能是极差即可

6029. 「雅礼集训 2017 Day1」市场

定义势能是极差即可*2

8.11 可持久化线段树与线段树合并

可持久化线段树

可持久化线段树用来在线求 \(\mathrm{mex}\)。8.9 介绍过了。

P2633 Count on a tree

P3567 [POI 2014] KUR-Couriers

此题还有随机化做法。

以上是简单题。


P3293 [SCOI2016] 美味

在值域上建立线段树,在序列维上可持久化。

首先 \(+x\) 只是整体平移了一段,在查询的时候处理一下就可以。按位考虑即可,差分查询就可以。复杂度是两只 \(\log\) 的。

P2839 [国家集训队] middle

典题,之前做过了。一个广为人知的 trick 是二分答案 \(mid\),然后 \(<mid\) 的赋成 \(-1\),\(\ge mid\) 的赋成 \(1\)。一个区间的中位数 \(\ge mid\) 等价于 \(1/-1\) 序列上的区间和是 \(\ge 0\)。

而且这道题启发了在序列维上建立线段树,在值域维上可持久化的方法。取决于我们更关心一个值出现的位置还是大小。

P7357 「PMOI-1」中位数

和上题几乎一样。唯一不同的是单点修改变为了区间修改,似乎这样子可持久化线段树就不能下放懒标记了,只能标记永久化。

P4587 [FJOI2016] 神秘数

妙妙题没有想出来。更重要的是想清楚一个确定集合的神秘数怎么求。从小到大考虑这个集合中的数,假设已经可以表示出 \([1,x]\),新加入的数是 \(a_i\)。如果 \(a_i > x + 1\),那么 \(x+1\) 肯定表示不出来,剩下的就不用看了。否则能表示的区间就变为 \([1,x+a_i]\)。

考虑优化这个过程,假设目前能表示出来 \([1,x]\) 中的所有数,设 \(\le x+1\) 的数的和是 \(res\),若 \(res\ge x+1\),那么令 \(x\gets res\)。否则答案就是 \(x+1\)。

用可持久化线段树维护就可以。每次查询时 \(x\) 增长是指数的,所以只会增长 \(\log\) 次,算上线段树查询,总复杂度是两只 \(\log\) 的。

线段树合并

P3521 [POI 2011] ROT-Tree Rotations

典题。两颗二叉树合为一个时,其内部节点顺序是不影响的当下的贡献的。用值域线段树的合并逐步模拟这个合并二叉树的过程即可,每次贪心的选使代价最少的方案。

P6773 [NOI2020] 命运

线段树合并以维护 dp 的好题。64pts 的朴素 dp 是很好像的。然后在深度维上开线段树维护就可以。这样做的原因是我们注意到总共只有 \(m\) 个限制,相对于 dp 的定义 \(dp_{u,dep}\) 的 \(O(n^2)\) 个点来说是很稀疏的,也就是势能很小。所以使用线段树合并。

线段树分裂

没有补完。原以为暑假后会有时间,现在看来是补不完了。

P5494 【模板】线段树分裂

模版

LOJ6189 Philosopher

颜色段均摊。颜色段的分裂与合并用线段树分裂和合并就可以。注意不可以使用平衡树,因为没法做到 \(O(\log n)\) 合并两个树。直接所有数乘起来显然会溢出,但是可以对每个数取 \(\lg\),然后区间加。不过没写过啊,不知道这样的精度够不够。

8.12 数据结构进阶

有很多很难的东西……自己也不太能够清晰的讲述出来。

KTT

学习文章是这个,很全面很清晰了。

U435624 【模板】KTT

一个可以警示后人的是,如果第二个 hack 创了,截距相同的直线应该选斜率更大的。

P5693 EI 的第六分块

也就是前一题的再推广。

CF1178G The Awesomest Vertex

首先将单点加,链求和转化成子树加,单点查询。也就是 dfn 序上区间加,查区间最大值,KTT 维护即可。

还有一个问题是有绝对值的话,有可能就不是区间加正数了。小 trick 是 \(\left | a \right | = \max(a,-a)\)。开两颗线段树,将绝对值去掉就可以了。现在还没来得及写,待补。


其实今天原本的名字是“专题:可持久化数据结构进阶”,但是我没看出来有什么和可持久化有关的东西。就当作 DS 大杂烩吧www剩下的都是一些杂题。

CF1515I Phoenix and Diamonds

很厉害的技巧是倍增分块。首先按题面给的贪心顺序给物品排序。当 \(c\) 落到 \([2^k,2^{k+1}-1]\) 中时,此时要么选足够多个 \(w<2^k\) 的物品,掉到下一层。或者选了若干个 \(w<2^k\) 的物品后碰到了 \(w\in [2^k,2^{k+1}-1]\) 的物品,选了掉到下一层。为每层开一个物品维的线段树,维护要选多少个 \(w<2^k\) 的物品才能掉到下一层,和通过选 \(w\in [2^k,2^{k+1}-1]\) 的物品掉到下一层至少 \(c\) 要多少。

P4234 最小差值生成树

将边权从小到大排序,二分答案,那么一组能选的边是一个区间,双指针搞一下,每个边有加入和删除的时间。线段树分治搞一下,复杂度有二分+线段树分治+并查集共两只 \(\log\) 和一个 \(\alpha\),不过这是可以过的。应该是今天最简单的一道题。

QOJ9708 Yuuki and a problem

如果记忆力好的话,你会立刻反应过来这就是 P4587 [FJOI2016] 神秘数 的带修加强版本。在那道题使用了主席树来解决,然而这题待修改所以只能用树套树维护。这也就是为什么树套树又叫做动态主席树。

下面这三道 Ynoi 第一期做过了。做这个可以尽情享受卡常的乐趣。

P5314 [Ynoi2011] ODT

距离为一的点是它自己、它父亲、它重儿子、它轻儿子。拿一个重链剖分和树状数组维护每个点的值。查询的时候它自己、它父亲、它重儿子都可以直接查出来。每个点再开一个平衡树维护轻儿子的权值。注意到一条链上只有 \(O(\log n)\) 个轻儿子,直接暴力该就可以。复杂度是重链剖分+树状数组 \(O(n\log^2 n)\) 的。

然而这样是过不去的。老师和题解会告诉你注意到查询是 \(O(\log n)\) 而修改是 \(O(\log^2n)\) 的。所以考虑平衡它们的复杂度。考虑记录每个点记录 \(p\) 个重儿子,那么修改是 \(O(\log_p n \log n)\) 的,查询是 \(O(p\log n)\) 的。平衡一下取 \(p=\frac{\log n}{\log\log n}\),得到总复杂度 \(O(n\frac{\log^2n}{\log\log n})\)。实际上本人十分辛苦的将上述过程实现后,把 \(p\) 的取值从 \(8\) 逐步往下调,结果发现 \(p\) 值越小越快……最后发现取一个重儿子就是最快的,当然同时也是过不了的,因为上述过程的常数很大,反而不如只取一个重儿子。

那怎么办呢?我现场学了一下常数极小的有旋 Treap,代替了原来无旋的 FHQ-Treap,直接过了……

P5311 [Ynoi2011] 成都七中

P7124 [Ynoi2008] stcm

8.13

考试。

先把大样例下下来,再浏览一遍题面。这个好习惯以后要带到 noip 与省选赛场上。发现 T3 是典题,但是忍住先不写,已经因为倒序开题崩盘过一次了。看 T1,数据范围很奇怪,随便写了写就过了。暴力枚举一维,另一维树状数组维护区间加,区间求和。\(m=50000,n=1000,O(mn\log n)\),过了。赛后发现二维树状数组维护矩形加,矩形和,可以做到 \(O(m\log^2 n)\),思想和一维是差不多的。

去写典的 T3。卡了一会大样例,发现题面错了,改完过了。xyd模拟赛的质量堪忧啊。去想最后的 T2,上厕所时想出了不带修改时的 \(O(n\log^2n)\) 的做法,带修的话,加线段树分治那不是三只 \(\log\) 了?没有前途所以写完了不带修改的特殊性质就跑了。\(100+55+100=255\)。T2 把二分线段树改为线段树二分就消掉一只 \(\log\),加上线段树分治就是 \(O(n\log^2n)\) 的,可以通过。这个没想到该罚。

T2 是 Gym103627A Points

T3 是 P3759 [TJOI2017] 不勤劳的图书管理员

posted @ 2026-10-29 18:57  Natho_nA  阅读(7)  评论(0)    收藏  举报