2.19 校内省选模拟赛(扫描线,历史版本和)

文章目录

  • 时间安排
  • 题解
  • [T2.「梦熊省选难度挑战赛 2023」树莓立方体](#T2.「梦熊省选难度挑战赛 2023」树莓立方体)

时间安排

一眼会了 T 1 T1 T1,先花 40 m i n 40min 40min 想了想 T 2 T2 T2,感觉会了 75 p t s 75pts 75pts。又回去看 T 1 T1 T1,先写了暴力,中间上了个厕所。最后将近 2 2 2 个半小时把 T 1 T1 T1 过了。拐回去写 T 2 T2 T2,写了 60 p t s 60pts 60pts,剩下 15 p t s 15pts 15pts 调不出来,急了,剩下 10 m i n 10min 10min 火速把 T 3 T3 T3 20 20 20 分过了。

最后得分:100 + 50 + 20 = 170
T 2 T2 T2 分段顺序写错了挂了 10 p t s 10pts 10pts。

题解

T2.「梦熊省选难度挑战赛 2023」树莓立方体

题意:

给定 k k k 行 n n n 列的正整数矩阵
( a i , j ) 1 ≤ i ≤ k , 1 ≤ j ≤ n (a_{i, j})_{1 \leq i \leq k,1 \leq j \leq n} (ai,j)1≤i≤k,1≤j≤n

有 q q q 个 询问任务,对每个询问任务 [ l , r ] [l, r] [l,r],需要计算一下公式:
∑ l ≤ x ≤ y ≤ r F ( max ⁡ i = 1 k ( min ⁡ j = x y a i , j ) ) \sum\limits_{l \leq x \leq y \leq r} F(\max\limits_{i = 1}^{k}(\min\limits_{j = x}^{y}a_{i, j})) l≤x≤y≤r∑F(i=1maxk(j=xminyai,j))

  • 其中 F ( M ) = A ⊕ ( B M + C ) F(M) = A \oplus (BM + C) F(M)=A⊕(BM+C), A , B , C A, B,C A,B,C 是三个非负整数的常数, ⊕ \oplus ⊕ 表示二进制按位异或。

1 ≤ k ≤ 20 1 \leq k \leq 20 1≤k≤20, 1 ≤ n ≤ 50000 1 \leq n \leq 50000 1≤n≤50000, 1 ≤ q ≤ 50000 1 \leq q \leq 50000 1≤q≤50000, 1 ≤ a i , j ≤ 1 0 7 1 \leq a_{i, j} \leq 10^7 1≤ai,j≤107, 0 ≤ A ≤ 1 0 8 0 \leq A \leq 10^8 0≤A≤108, 0 ≤ B , C ≤ 10 0 \leq B,C \leq 10 0≤B,C≤10

分析:

感觉给出的 F F F 函数就是阻止你按位做,实际上本题确实不能按位考虑。

考虑从大到小插入每一个数字。假设每次插入 ( x , y ) (x, y) (x,y) 位置上的数字后, ( x , y ) (x, y) (x,y) 所在行已经插入的数字形成连通块中包含 ( x , y ) (x, y) (x,y) 的左右端点为 [ L , R ] [L, R] [L,R]。那么我们将所有区间 [ l , r ] [l, r] [l,r] 满足 L ≤ l ≤ r ≤ R L \leq l \leq r \leq R L≤l≤r≤R 遍历 。注意到一个区间 [ l , r ] [l, r] [l,r] 第一次被遍历的时刻是这 k k k 行 [ l , r ] [l, r] [l,r] 区间最小值的最大值被插入的时刻。

将一个区间 [ l , r ] [l, r] [l,r] 看作平面上 y y y 轴 和直线 y = x y = x y=x 之间的一个点。那么每次插入一个数 a x , y a_{x, y} ax,y 后就相当于把 点 ( L , R ) (L, R) (L,R) 的右下角所有还未被标记的点 标记成 F ( a x , y ) F(a_{x, y}) F(ax,y)。这里先不用考虑 y = x y = x y=x 下方的点,当它们存在即可。每个询问 [ l , r ] [l, r] [l,r] 就是问 ( l , r ) (l, r) (l,r) 右下角所有点的标记之和。

由于每次都是标记一个点所有右下角的点,因此任意时刻所有被标记的点都呈现了 阶梯状 。维护阶梯的顶点,然后每次插入 ( L , R ) (L, R) (L,R) 时可以根据这些顶点将右下角未被标记的区域拆分成若干矩形。注意到 每多拆一个矩形就会删掉一个顶点,因此矩形总个数均摊 O ( n k ) O(nk) O(nk)

求出来所有的矩形后,问题变成了:有 n n n 次矩形加, q q q 次询问一个矩形内的和。

做法是将询问的矩形容斥成 4 4 4 个左上角固定(都在平面的左上方)的矩形,然后从左到右扫描列,对每一行维护 历史版本和 。在每一列对相应的矩形求答案。可以扫到一列就把不合法的行( y = x y = x y=x 下方)删除掉,这样就不会算到不合法的点的贡献了。

可以用 s e t set set 维护矩形端点,用线段树 + + + ( + , ∗ ) (+,*) (+,∗)矩乘 维护历史版本和

时间复杂度 O ( ( n k + q ) × log ⁡ 2 n × 3 3 ) O((nk + q)\times \log_2n \times 3^3) O((nk+q)×log2n×33),有点卡常。

这里放一下我的 8.5 k 8.5k 8.5k 屎山代码:

cpp 复制代码
// 首先有一个问题:若干矩形加,求单个矩形和
// 扫描线,维护历史版本和即可 
#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;
}
相关推荐
吴梓穆33 分钟前
UE5学习笔记 FPS游戏制作43 UI材质
笔记·学习·ue5
aaaweiaaaaaa33 分钟前
蓝桥杯c ++笔记(含算法 贪心+动态规划+dp+进制转化+便利等)
c语言·数据结构·c++·算法·贪心算法·蓝桥杯·动态规划
Hesse36 分钟前
希尔排序:Python语言实现
python·算法
密码小丑1 小时前
玄机-apache日志分析
网络·笔记·apache
XYN611 小时前
【嵌入式面试】
笔记·python·单片机·嵌入式硬件·学习
h^hh1 小时前
pipe匿名管道实操(Linux)
数据结构·c++·算法
dr李四维1 小时前
解决缓存穿透的布隆过滤器与布谷鸟过滤器:谁更适合你的应用场景?
redis·算法·缓存·哈希算法·缓存穿透·布隆过滤器·布谷鸟过滤器
亓才孓1 小时前
[leetcode]01背包问题
算法·leetcode·职场和发展
学习编程的gas2 小时前
数据结构——二叉树
数据结构·算法
its_a_win3 小时前
蓝桥杯 2023省B 飞机降落 dfs
c++·算法·蓝桥杯