2.19 校内省选模拟赛(扫描线,历史版本和)
时间安排
一眼会了 \(T1\),先花 \(40min\) 想了想 \(T2\),感觉会了 \(75pts\)。又回去看 \(T1\),先写了暴力,中间上了个厕所。最后将近 \(2\) 个半小时把 \(T1\) 过了。拐回去写 \(T2\),写了 \(60pts\),剩下 \(15pts\) 调不出来,急了,剩下 \(10min\) 火速把 \(T3\) \(20\) 分过了。
最后得分:100 + 50 + 20 = 170
\(T2\) 分段顺序写错了挂了 \(10pts\)。
题解
T2.「梦熊省选难度挑战赛 2023」树莓立方体
题意:
给定 \(k\) 行 \(n\) 列的正整数矩阵
有 \(q\) 个 询问任务,对每个询问任务 \([l, r]\),需要计算一下公式:
- 其中 \(F(M) = A \oplus (BM + C)\), \(A, B,C\) 是三个非负整数的常数, \(\oplus\) 表示二进制按位异或。
\(1 \leq k \leq 20\), \(1 \leq n \leq 50000\), \(1 \leq q \leq 50000\),\(1 \leq a_{i, j} \leq 10^7\), \(0 \leq A \leq 10^8\),\(0 \leq B,C \leq 10\)
分析:
感觉给出的 \(F\) 函数就是阻止你按位做,实际上本题确实不能按位考虑。
考虑从大到小插入每一个数字。假设每次插入 \((x, y)\) 位置上的数字后, \((x, y)\) 所在行已经插入的数字形成连通块中包含 \((x, y)\) 的左右端点为 \([L, R]\)。那么我们将所有区间 \([l, r]\) 满足 \(L \leq l \leq r \leq R\) 遍历。注意到一个区间 \([l, r]\) 第一次被遍历的时刻是这 \(k\) 行 \([l, r]\) 区间最小值的最大值被插入的时刻。
将一个区间 \([l, r]\) 看作平面上 \(y\) 轴 和直线 \(y = x\) 之间的一个点。那么每次插入一个数 \(a_{x, y}\) 后就相当于把 点 \((L, R)\) 的右下角所有还未被标记的点标记成 \(F(a_{x, y})\)。这里先不用考虑 \(y = x\) 下方的点,当它们存在即可。每个询问 \([l, r]\) 就是问 \((l, r)\) 右下角所有点的标记之和。
由于每次都是标记一个点所有右下角的点,因此任意时刻所有被标记的点都呈现了 阶梯状。维护阶梯的顶点,然后每次插入 \((L, R)\) 时可以根据这些顶点将右下角未被标记的区域拆分成若干矩形。注意到 每多拆一个矩形就会删掉一个顶点,因此矩形总个数均摊 \(O(nk)\)。
求出来所有的矩形后,问题变成了:有 \(n\) 次矩形加, \(q\) 次询问一个矩形内的和。
做法是将询问的矩形容斥成 \(4\) 个左上角固定(都在平面的左上方)的矩形,然后从左到右扫描列,对每一行维护 历史版本和。在每一列对相应的矩形求答案。可以扫到一列就把不合法的行( \(y = x\) 下方)删除掉,这样就不会算到不合法的点的贡献了。
可以用 \(set\) 维护矩形端点,用线段树 \(+\) \((+,*)\)矩乘 维护历史版本和
时间复杂度 \(O((nk + q)\times \log_2n \times 3^3)\),有点卡常。
这里放一下我的 \(8.5k\) 屎山代码:
// 首先有一个问题:若干矩形加,求单个矩形和
// 扫描线,维护历史版本和即可
#include<bits/stdc++.h>
#define MP make_pair
#define pb emplace_back
using namespace std;
struct IO{
static const int S=1<<21;
char buf[S],*p1,*p2;int st[105],Top;
~IO(){clear();}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='\r');return *this;}
template<typename T>inline IO&operator >> (T&x){
x=0;bool f=0;char ch=gc();
while(!isdigit(ch)){if(ch=='-') f^=1;ch=gc();}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
f?x=-x:0;return *this;
}
inline IO&operator << (const char c){pc(c);return *this;}
template<typename T>inline IO&operator << (T x){
if(x<0) pc('-'),x=-x;
do{st[++st[0]]=x%10,x/=10;}while(x);
while(st[0]) pc('0'+st[st[0]--]);return *this;
}
}fin,fout;
const int M = 22;
const int N = 50010;
typedef long long LL;
typedef pair< int, int > PII;
int m, n, q, odr[N * M];
LL A, B, C, a[M][N], ans[N];
struct qry {
int x, idx, f;
};
vector< qry > qq[N];
inline LL F(LL x) {return (A ^ (B * x + C));}
namespace Square {
struct matrix {
LL mt[3][3];
friend matrix operator * (const matrix a, const matrix b) {
matrix c;
c.mt[0][0] = a.mt[0][0] * b.mt[0][0] + a.mt[0][1] * b.mt[1][0] + a.mt[0][2] * b.mt[2][0];
c.mt[0][1] = a.mt[0][0] * b.mt[0][1] + a.mt[0][1] * b.mt[1][1] + a.mt[0][2] * b.mt[2][1];
c.mt[0][2] = a.mt[0][0] * b.mt[0][2] + a.mt[0][1] * b.mt[1][2] + a.mt[0][2] * b.mt[2][2];
c.mt[1][0] = a.mt[1][0] * b.mt[0][0] + a.mt[1][1] * b.mt[1][0] + a.mt[1][2] * b.mt[2][0];
c.mt[1][1] = a.mt[1][0] * b.mt[0][1] + a.mt[1][1] * b.mt[1][1] + a.mt[1][2] * b.mt[2][1];
c.mt[1][2] = a.mt[1][0] * b.mt[0][2] + a.mt[1][1] * b.mt[1][2] + a.mt[1][2] * b.mt[2][2];
c.mt[2][0] = a.mt[2][0] * b.mt[0][0] + a.mt[2][1] * b.mt[1][0] + a.mt[2][2] * b.mt[2][0];
c.mt[2][1] = a.mt[2][0] * b.mt[0][1] + a.mt[2][1] * b.mt[1][1] + a.mt[2][2] * b.mt[2][1];
c.mt[2][2] = a.mt[2][0] * b.mt[0][2] + a.mt[2][1] * b.mt[1][2] + a.mt[2][2] * b.mt[2][2];
return c;
}
friend matrix operator + (const matrix a, const matrix b) {
matrix c;
c.mt[0][0] = a.mt[0][0] + b.mt[0][0];
c.mt[0][1] = a.mt[0][1] + b.mt[0][1];
c.mt[0][2] = a.mt[0][2] + b.mt[0][2];
c.mt[1][0] = a.mt[1][0] + b.mt[1][0];
c.mt[1][1] = a.mt[1][1] + b.mt[1][1];
c.mt[1][2] = a.mt[1][2] + b.mt[1][2];
c.mt[2][0] = a.mt[2][0] + b.mt[2][0];
c.mt[2][1] = a.mt[2][1] + b.mt[2][1];
c.mt[2][2] = a.mt[2][2] + b.mt[2][2];
return c;
}
friend bool operator != (const matrix a, matrix b) {
if(a.mt[0][0] != b.mt[0][0]) return 1;
if(a.mt[0][1] != b.mt[0][1]) return 1;
if(a.mt[0][2] != b.mt[0][2]) return 1;
if(a.mt[1][0] != b.mt[1][0]) return 1;
if(a.mt[1][1] != b.mt[1][1]) return 1;
if(a.mt[1][2] != b.mt[1][2]) return 1;
if(a.mt[2][0] != b.mt[2][0]) return 1;
if(a.mt[2][1] != b.mt[2][1]) return 1;
if(a.mt[2][2] != b.mt[2][2]) return 1;
return 0;
}
} Mt, I;
struct Seg {
struct SegmentTree {
int l, r;
matrix mat, tag;
#define l(x) t[x].l
#define r(x) t[x].r
#define mat(x) t[x].mat
#define tag(x) t[x].tag
}t[N * 4];
void update(int p) {mat(p) = mat(p << 1) + mat(p << 1 | 1);}
void spread(int p) {
if(tag(p) != I) {
mat(p << 1) = mat(p << 1) * tag(p); tag(p << 1) = tag(p << 1) * tag(p);
mat(p << 1 | 1) = mat(p << 1 | 1) * tag(p); tag(p << 1 | 1) = tag(p << 1 | 1) * tag(p);
tag(p) = I;
}
}
void build(int p, int l, int r) {
l(p) = l, r(p) = r; tag(p) = I;
if(l == r) {
mat(p).mt[0][0] = mat(p).mt[0][1] = 0;
mat(p).mt[0][2] = 1;
return ;
}
int mid = (l + r >> 1);
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
update(p);
}
void change(int p, int l, int r, matrix c) {
if(l <= l(p) && r >= r(p)) {
tag(p) = tag(p) * c;
mat(p) = mat(p) * c;
return ;
}
spread(p);
int mid = (l(p) + r(p) >> 1);
if(l <= mid) change(p << 1, l, r, c);
if(r > mid) change(p << 1 | 1, l, r, c);
update(p);
}
void clr(int p, int pos) {
if(pos < l(p) || pos > r(p)) return ;
if(l(p) == r(p)) {
mat(p).mt[0][1] = 0, mat(p).mt[0][2] = 0;
return ;
}
spread(p);
int mid = (l(p) + r(p) >> 1);
if(pos <= mid) clr(p << 1, pos);
else clr(p << 1 | 1, pos);
update(p);
}
LL ask(int p, int l, int r) {
if(l <= l(p) && r >= r(p)) return mat(p).mt[0][0];
spread(p);
int mid = (l(p) + r(p) >> 1);
if(r <= mid) return ask(p << 1, l, r);
else if(l > mid) return ask(p << 1 | 1, l, r);
else return ask(p << 1, l, r) + ask(p << 1 | 1, l, r);
}
} T;
struct line {
int x, u, d; // u > d
LL v;
}L[N * M * 10];
int tot;
int bin[N * M], mn[N * M], mx[N * M];
bool bok[M][N];
set< PII > s;
inline int X(int x) {return x % n == 0 ? x / n : x / n + 1;};
inline int Y(int x) {return x % n == 0 ? n : x % n;}
int Find(int x) {return x == bin[x] ? x : bin[x] = Find(bin[x]);}
inline void Merge(int x, int y) {
int f1 = Find(x), f2 = Find(y);
if(f1 != f2) {bin[f1] = f2; mn[f2] = min(mn[f2], mn[f1]); mx[f2] = max(mx[f2], mx[f1]);}
}
bool cmp(int x, int y) {return a[X(x)][Y(x)] > a[X(y)][Y(y)];}
bool ccmp(line a, line b) {return a.x < b.x;};
inline void ins_square(int xl, int yl, int xr, int yr, LL v) {
if(xl > xr || yl < yr) return ;
L[++ tot] = (line) {xl, yl, yr, v};
L[++ tot] = (line) {xr + 1, yl, yr, -v};
}
void ins(int x, int y, LL v) { // 插入一个阶梯状拐点
if(s.empty()) {
s.insert(MP(x, y));
ins_square(x, y, n + 1, 0, v);
return ;
}
auto itl = s.lower_bound(MP(x, y));
if(itl == s.end()) { // 找不到 左边肯定有一个
itl --;
if(itl->second >= y) return ; // 没必要插了
ins_square(x, y, n + 1, itl->second + 1, v);
s.insert(MP(x, y));
return ;
}
if(itl->first == x) return ; // 已经包含了没必要插
auto itr = itl;
if(itl != s.begin()) { // 先把左边处理了
int tx = (itl->first); itl --;
if(itl->second >= y) return ;
int gy = itl->second;
if(itl->first < x) itl ++; // 如果等于 x,那么需要把它删掉
ins_square(x, y, tx - 1, gy + 1, v);
}
else {
int tx = (itl->first);
ins_square(x, y, tx - 1, 0, v);
}
while(itr->second <= y) {
int tx = (itr->first); int ty = (itr->second);
itr ++;
if(itr != s.end()) {
int nxtx = (itr->first);
ins_square(tx, y, nxtx - 1, ty + 1, v);
}
else {
ins_square(tx, y, n + 1, ty + 1, v);
break;
}
}
s.erase(itl, itr);
s.insert(MP(x, y));
}
inline matrix get_matrix(LL v) {
matrix res;
res.mt[0][0] = 1; res.mt[0][1] = 0; res.mt[0][2] = 0;
res.mt[1][0] = 0; res.mt[1][1] = 1; res.mt[1][2] = 0;
res.mt[2][0] = 0; res.mt[2][1] = v; res.mt[2][2] = 1;
return res;
}
void get() {
for(int i = 1; i <= n * m; i ++ ) bin[i] = i, mn[i] = mx[i] = Y(i);
sort(odr + 1, odr + n * m + 1, cmp);
for(int i = 1; i <= n * m; i ++ ) {
int o = odr[i];
int x = X(o), y = Y(o);
bok[x][y] = 1;
if(bok[x][y - 1]) Merge((x - 1) * n + y - 1, o);
if(bok[x][y + 1]) Merge((x - 1) * n + y + 1, o);
int f = Find(o);
ins(mn[f], mx[f], F(a[x][y]));
}
}
inline void scan() { // 扫描线, 需要维护历史版本和
for(int i = 0; i < 3; i ++ ) for(int j = 0; j < 3; j ++ ) I.mt[i][j] = (i == j);
Mt.mt[0][0] = 1; Mt.mt[1][0] = 1;
Mt.mt[1][1] = 1; Mt.mt[2][2] = 1;
sort(L + 1, L + tot + 1, ccmp);
T.build(1, 0, n);
int p = 1;
for(int i = 1; i <= n; i ++ ) { // 扫描每一列
T.clr(1, i - 1);
while(p <= tot && L[p].x <= i) { // 先变换
T.change(1, L[p].d, L[p].u, get_matrix(L[p].v));
p ++;
}
T.change(1, 0, n, Mt);
for(auto v : qq[i]) {
int x = v.x, idx = v.idx, f = v.f;
ans[idx] += T.ask(1, x, n) * f;
}
}
}
}
int main() {
freopen("plant.in", "r", stdin);
freopen("plant.out", "w", stdout);
fin >> m >> n >> q;
for(int i = 1; i <= m; i ++ )
for(int j = 1; j <= n; j ++ ) {
fin >> a[i][j];
odr[(i - 1) * n + j] = (i - 1) * n + j;
}
fin >> A >> B >> C;
for(int i = 1; i <= q; i ++ ) {
int l, r; fin >> l >> r;
qq[r].pb((qry) {l, i, 1});
if(r + 1 <= n) qq[r].pb((qry) {r + 1, i, -1});
if(l - 1 >= 1) qq[l - 1].pb((qry) {l, i, -1});
if(l - 1 >= 1 && r + 1 <= n) qq[l - 1].pb((qry) {r + 1, i, 1});
}
Square::get();
Square::scan();
for(int i = 1; i <= q; i ++ ) fout << ans[i], fout.pc('\n');
return 0;
}

浙公网安备 33010602011771号