洛谷 P13826 [Ynoi Easy Round 2026] 寒蝉鸣泣之时 题解
前言
传送门。
毒瘤。
题意
给定 \(n\) 个边平行于坐标轴的平面矩形,以及正整数 \(m\),对 \(1\le m\cdot i\le n\) 的每个整数 \(i\) ,你需要计算出恰好被 \(m\cdot i\) 个矩形包含的区域的面积。
思路
矩形面积问题我们可以考虑扫描线。扫掉一维之后,我们需要思考一下用什么数据结构维护另一维。这个查询要求长得来者不善的样子,看起来用树形结构完全做不了,我们考虑分块。
用分块去维护什么呢?一个朴素的思路是我们去存储所有值 \(v\) 的出现次数 \(cnt_v\)。求对答案的贡献时,我们只需要看 \(v\) 是否是 \(m\) 的倍数,如果是的话,是多少倍,然后将 \(cnt_v\) 贡献给这个倍数的答案即可。
但是这样做显然太暴力了,每次算贡献都是 \(O(n)\) 的。我们还需要优化。
我们考虑一个值 \(v\) 会对倍数 \(t\) 产生贡献,当且仅当 \(v+tag=mt\)。这里 \(tag\) 就是对应块的增量标记。简单变形我们得到 \(t=\frac{v+tag}{m}\)。所以我们如果知道这一块 \(tag\) 的最小值、最大值的话,我们就可以得到一个 \(t\) 的上界和下界。设极差为 \(R\),那么单次计算贡献的复杂度就优化到了 \(O(\frac{R}{m}\sqrt{n})\)。由于扫描线的性质,\(tag\) 的变化量每次最多是 \(1\),我们考察某一个块,其过程中所有 \(R\) 的总和也就是 \(n\),所以对于每一块,整个过程中因计算贡献而产生的复杂度是 \(O(n)\)。这就可以接受了。所以我们对每一块额外维护一下 \(tag\) 的最值,以及每个 \(tag\) 的出现次数就可以。
然后就是 Ynoi 的传统艺能,我们来进行一个精细卡常。
先看空间。我们分析发现复杂度瓶颈在于,我们需要用二维数组 cnt[B][N] 维护每块中每个 \(tag\) 的出现次数。我们发现这个出现次数是 \(O(n)\) 级别的,且非负,用 int 存显然太浪费了。我们完全可以进行一个拆分,用 unsigned short 来存下低 \(16\) 位,用 unsigned char 来存剩下的高位。这样空间就是原来的 \(\frac{3}{4}\),计算一下发现是 OK 的。
再看时间。这里其实没什么大的优化,就是一些小细节。详见代码。
代码
#include<cstdio>
#include<vector>
#include<cmath>
#include<tuple>
#define ll long long
#define MIKU 0
using namespace std;
//快读快写。
#define SUBMIT
namespace Hatsune {
#ifdef SUBMIT
#define SIZE (1<<22)
char in[SIZE], out[SIZE], *p1 = in, *p2 = in, *p3 = out;
#define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,SIZE,stdin),p1==p2)?EOF:*p1++)
#define flush() (fwrite(out,1,p3-out,stdout),p3=out)
#define putchar(ch) (p3==out+SIZE&&flush(),*p3++=(ch))
class Flush { public:~Flush() { flush(); } }_;
#endif
namespace Miku {
template<typename type>
inline void read(type& x) {
x = 0; bool flag(0); char ch = getchar();
while(ch < '0' || ch>'9') flag ^= ch == '-', ch = getchar();
while(ch >= '0' && ch <= '9') x = (x<<1)+(x<<3)+(ch^48), ch = getchar();
flag ? x = -x : 0;
}
template<typename type, typename ...T>
inline void read(type &x, T&...y) { read(x); read(y...); }
/*- read & write -*/
template<typename type>
inline void write(type x, bool flag) {
x < 0 ? x = -x, putchar('-') : 0; short Stack[50], top(0);
do Stack[++top] = x % 10, x /= 10; while (x);
while(top) putchar(Stack[top--] | 48);
flag ? putchar('\n') : putchar(' ');
}
}
#ifdef SUBMIT
#undef SIZE
#undef getchar
#undef putchar
#undef flush
#endif
}using Hatsune::Miku::read; using Hatsune::Miku::write;
const int N = 3e5 + 5, B = 550, inf = 0x3f3f3f3f;
int n, m;
ll ans[N];
vector<tuple<int, int, int> > ed[2*N];
//拆分 int 存储。
unsigned short cnt_lo[B][N];
unsigned char cnt_hi[B][N];
//函数宏定义加速。
#define cnt_set(i, j) (cnt_lo[i][j] = cnt_hi[i][j] = 0)
#define cnt_add(i, j) (cnt_lo[i][j] ++, cnt_hi[i][j] += cnt_lo[i][j] ? 0 : 1)
#define cnt_get(i, j) (((int)cnt_hi[i][j]<<16) + (int)cnt_lo[i][j])
struct Block {
int siz, tot;
int op[B], ed[B], add[B], mx[B], mn[B], pos[N], val[N];
void init() {
siz = sqrt(n); tot = (n-1) / siz + 1;
for(int i=1; i<=tot; i++) {
op[i] = (i-1) * siz + 1;
ed[i] = i * siz;
} ed[tot] = n;
for(int i=1; i<=n; i++) pos[i] = (i-1) / siz + 1;
}
void calc(int b) {
if(mx[b] >= mn[b]) {
for(int i=op[b]; i<=ed[b]; i++) {
int v = val[i];
//算一下 t 的上下界。
int tl = max(1, (v+mn[b]-1)/m+1), tr = min(n/m, (v+mx[b])/m);
for(int t=tl; t<=tr; t++) ans[t] += cnt_get(b, m*t-v);
}
for(int i=mn[b]; i<=mx[b]; i++) cnt_set(b, i);
mx[b] = -inf; mn[b] = inf;
}
}
void update(int l, int r, int v) {
int L = pos[l], R = pos[r];
if(L == R) {
calc(L);
for(int i=l; i<=r; i++) val[i] += v;
} else {
calc(L); calc(R);
for(int i=l; i<=ed[L]; i++) val[i] += v;
for(int i=L+1; i<R; i++) add[i] += v;
for(int i=op[R]; i<=r; i++) val[i] += v;
}
}
} blc;
int main() {
read(n, m); blc.init();
for(int i=1; i<=n; i++) {
int x_1, x_2, y_1, y_2; read(x_1, x_2, y_1, y_2);
ed[x_1].emplace_back(y_1, y_2-1, 1); //比 push_back 更快。
ed[x_2].emplace_back(y_1, y_2-1, -1);
}
for(int i=1; i<=n; i++) {
for(auto it : ed[i]) {
int itl, itr, itv; tie(itl, itr, itv) = it;
blc.update(itl, itr, itv);
}
for(int b=1; b<=blc.tot; b++) {
cnt_add(b, blc.add[b]);
blc.mn[b] = min(blc.mn[b], blc.add[b]);
blc.mx[b] = max(blc.mx[b], blc.add[b]);
}
}
for(int i=1; i<=blc.tot; i++) blc.calc(i);
for(int i=1; i*m<=n; i++) write(ans[i], 1);
return MIKU;
}

浙公网安备 33010602011771号