zoukankan      html  css  js  c++  java
  • 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) 的,更新答案。

  • 相关阅读:
    【转】C# 注册表简单操作
    [转]VC++之随父窗口变化调整控件大小/位置
    [转]vc 汉字汉语拼音首字母如何获取
    [转]浅析ActiveX控件的CAB压缩
    [转]VC6.0中使用MFC开发ActiveX及简单验证
    ElasticSearch增加索引字段
    单词缩写
    linux命令
    使用PR插件Twixtor Pro对视频补帧
    Linux创建脚本服务
  • 原文地址:https://www.cnblogs.com/Shinomiya/p/15329212.html
Copyright © 2011-2022 走看看