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\) 列的正整数矩阵

\[(a_{i, j})_{1 \leq i \leq k,1 \leq j \leq n} \]

\(q\) 个 询问任务,对每个询问任务 \([l, r]\),需要计算一下公式:

\[\sum\limits_{l \leq x \leq y \leq r} F(\max\limits_{i = 1}^{k}(\min\limits_{j = x}^{y}a_{i, j})) \]

  • 其中 \(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;
}

posted @ 2026-09-10 20:23  Czl2007  阅读(5)  评论(0)    收藏  举报