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",n);
else if(ck.second < x) printf("%d\n",n-ck.second);
else printf("%d\n",n-x+1);
break;
}
}
}
return 0;
}
关于 \(T_4\),确实是个分块。这里就因为时间较紧,先着重研究提高级内容了。
正解就是,按 \(\sqrt N\) 分块,\(k\ge \sqrt N\) 的就暴力修改,最后处理一下所有 \(k<\sqrt N\) 的,更新答案。