2021.09.23模拟赛

2021.09.23 模拟赛

下午,受困于 AC自动机 的深潭中时,得知要参加模拟赛。

下午还是脑子迷乱,状态不太好……

赛时

开场看题,注意到 -O2 -std=c++14.

粗略扫了一遍题:

(T_1) : 构造矩阵,求对角线和。但是优化需要数学?

(T_2) : 乍一看是图论。注意到有加边和删边,无法用图来解决。

(T_3) : 期望问题……不会……

(T_4) : 是个分块吧,但是分块不是 NOI 级内容吗……?

一圈看下来,感觉只有 (T_2)(T_4) 可做,

先攻 (T_2) 。(用了大概 3 个小时)

大概想到:

从前向后连的边是无用的,直接忽略。
从后向前的边,等价于将 ([X,Y]) 中的点变为联通。
删掉一条边,有点不太会搞。

但是?

考虑从后向前的边:将 ([X,Y]) 的点变为联通,可以转化为将 ([X,Y]) 的点打上 (+1) 的标记。
那么,删边相当于将 ([X,Y]) 内的点打上 (-1) 的标记。

考虑联通性,即是找到包含 (X) 的标记最左端点。

这个过程二分解决。

但是,受困在标记最左端点这里(指卡了 3 个小时),没有想出线段树如何处理区间内有多少个 (0) 的问题。

剩下大概半个小时,写了下 (T_4) 的线段树暴力。

赛后

(T_1)(T_3) 真的是纯数学……

(T_2) 的思路就是正解思路。后来在研究下发现,只需要线段树支持区间修改、统计区间内最小值及其出现的位置即可……

这里是思维没有打开的问题了……

可以类比之前的扫描线问题。同样是区间 (+1,-1) ,统计区间内 (0) 的问题,因为当时没有太仔细考虑,且赛时思路没有打开,导致此题想出了正解却没有写出。

以下是自己赛后的 AC 代码。

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f , N = 5e5+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0; char ch = ' ', c = getchar();
	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
	while(c>='0'&&c<='9') ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,m;
struct Segtre{int min,laz,pos;}tre[N<<2];
inline void pushup(int k){
	tre[k].min = min(tre[k<<1].min , tre[k<<1|1].min);
	tre[k].pos = tre[k<<1].min < tre[k<<1|1].min ? tre[k<<1].pos : tre[k<<1|1].pos;
}
inline void mod(int k,int l,int r,int w){
	tre[k].min += w,
	tre[k].laz += w;
}
inline void pushdown(int k,int l,int r){
	if(!tre[k].laz) return;
	int mid = (l + r) >> 1;
	mod(k<<1,l,mid,tre[k].laz),
	mod(k<<1|1,mid+1,r,tre[k].laz);
	tre[k].laz = 0;
}
void build(int k,int l,int r){
	if(l == r){tre[k] = (Segtre){0,0,l}; return;}
	int mid = (l + r) >> 1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}
void modify(int k,int l,int r,int x,int y,int w){
	if(x <= l && r <= y) {mod(k,l,r,w); return;}
	pushdown(k,l,r);
	int mid = (l + r) >> 1;
	if(x <= mid) modify(k<<1,l,mid,x,y,w);
	if(y > mid)  modify(k<<1|1,mid+1,r,x,y,w);
	pushup(k);
}
pair<int,int> query(int k,int l,int r,int x,int y){
	if(x <= l && r <= y) return make_pair(tre[k].min,tre[k].pos);
	pushdown(k,l,r);
	int mid = (l + r) >> 1;
	if(y <= mid) return query(k<<1,l,mid,x,y);
	if(x > mid)  return query(k<<1|1,mid+1,r,x,y);
	pair<int,int> ls = query(k<<1,l,mid,x,y), rs = query(k<<1|1,mid+1,r,x,y);
	return make_pair(min(ls.first,rs.first), (ls.first < rs.first ? ls.second : rs.second) );
}
signed main(){
//	fo("build");
	n = read(), m = read();
	build(1,1,n);
	while(m--){
		int op = read();
		switch(op){
			case 1:{
				int r = read() , l = read();
				if(l >= r) continue;
				modify(1,1,n,l,r,1);
				break;
			}
			case 2:{
				int r = read() , l = read();
				if(l >= r) continue;
				modify(1,1,n,l,r,-1);
				break;
			}
			default:{
				int x = read();
				pair<int,int> ck = query(1,1,n,1,x);
				if(ck.first) printf("%d
",n);
				else if(ck.second < x) printf("%d
",n-ck.second);
				else printf("%d
",n-x+1);
				break;
			}
		}
	}
	return 0;
}

关于 (T_4),确实是个分块。这里就因为时间较紧,先着重研究提高级内容了。

正解就是,按 (sqrt N) 分块,(kge sqrt N) 的就暴力修改,最后处理一下所有 (k<sqrt N) 的,更新答案。

原文地址:https://www.cnblogs.com/Shinomiya/p/15329212.html