洛谷 P2824 [HEOI2016/TJOI2016] 排序 做题记录

前置芝士:线段树、二分

思路

很有意思的一道题。

这道题问的是位置 q 上的数字。特殊就特殊在这里,因为只有一次询问,只问一个位置。

在开始之前我们先想想,对于一个 01 串排序,我们要怎么做?

  • 如果是升序排序,那么把所有的 1 放在后面,所有的 0 放在前面。
  • 如果是降序排序,那么就是反着来。

看懂了这一点,那么我们就明白了。可以二分最后位置上的答案大于等于某个值,很明显这个答案是有单调性的。我们设二分出来的值为 x。将原数组中大于等于这个数的位置设为 1,否则设为 0。然后对于每一次排序,就相当于在区间询问,区间修改,用线段树解决即可。

时间复杂度(默认 n,m 同阶):O(nlog2n)
难点/坑点:如果一个区间只剩 1/0,那么不用修改,直接下一个即可。

点击查看代码
#include<bits/stdc++.h>

#define ll long long
#define i128 __int128

#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define fst first 
#define scd second 
#define dbg puts("IAKIOI")

using namespace std;

int read() {
	int x=0,f=1; char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); 
	for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }

const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }

#define maxn 100050

int n,m;

struct SegT {
	struct node {
		int sum,tag;
	}tr[maxn<<2];
	void psu(int idx) {
		tr[idx].sum=tr[lc(idx)].sum+tr[rc(idx)].sum;
	}
	void psd(int l,int r,int idx) {
		if(tr[idx].tag==-1) return ;
		int mid=(l+r)>>1;
		tr[lc(idx)].sum=tr[idx].tag*(mid-l+1);
		tr[rc(idx)].sum=tr[idx].tag*(r-mid);
		tr[lc(idx)].tag=tr[idx].tag;
		tr[rc(idx)].tag=tr[idx].tag;
		tr[idx].tag=-1;
	}
	void build(int idx,int l,int r,int a[]) {
		tr[idx].tag=-1;
		if(l==r) {
			tr[idx].sum=a[l];
			return ;
		}
		int mid=(l+r)>>1;
		build(lc(idx),l,mid,a);
		build(rc(idx),mid+1,r,a);
		psu(idx);
	}
	int query(int idx,int l,int r,int L,int R) {
//		cout<<idx<<' '<<l<<' '<<r<<' '<<L<<' '<<R<<'\n';
		if(L<=l&&r<=R) {
			return tr[idx].sum;
		}
		int mid=(l+r)>>1,res=0;
		psd(l,r,idx);
		if(L<=mid) res=query(lc(idx),l,mid,L,R);
		if(R>mid) res+=query(rc(idx),mid+1,r,L,R);
		return res;
	}
	void upd(int idx,int l,int r,int L,int R,int val) {
//		cout<<idx<<' '<<l<<' '<<r<<' '<<L<<' '<<R<<'\n';
		if(L<=l&&r<=R) {
			tr[idx].sum=(r-l+1)*val;
			tr[idx].tag=val;
			return ;
		}
		psd(l,r,idx);
		int mid=(l+r)>>1;
		if(L<=mid) upd(lc(idx),l,mid,L,R,val);
		if(R>mid) upd(rc(idx),mid+1,r,L,R,val);
		psu(idx);
	}
}Tr;

struct qu {
	int opt,l,r;
}q[maxn];
int a[maxn],b[maxn];
int pos;

bool check(int x) {
//	cout<<x<<" qwq\n";
	For(i,1,n) b[i]=(a[i]>=x);
//	For(i,1,n) cout<<b[i]<<' '; puts("");
	Tr.build(1,1,n,b);
	For(i,1,m) {
		int tot=Tr.query(1,1,n,q[i].l,q[i].r);
//		cout<<tot<<'\n';
		if(tot==0||tot==q[i].r-q[i].l+1) continue;
		if(q[i].opt==1) {
			Tr.upd(1,1,n,q[i].l,q[i].l+tot-1,1);
			Tr.upd(1,1,n,q[i].l+tot,q[i].r,0);		
		} else {
			Tr.upd(1,1,n,q[i].r-tot+1,q[i].r,1);
			Tr.upd(1,1,n,q[i].l,q[i].r-tot,0);
		}
//		For(j,1,n) cout<<Tr.query(1,1,n,j,j)<<' '; puts("");
	}
	return Tr.query(1,1,n,pos,pos);
}

void work() {
	in2(n,m);
	For(i,1,n) in1(a[i]);
	For(i,1,m) in3(q[i].opt,q[i].l,q[i].r);
	in1(pos);
	int l=1,r=n;
	while(l<r) {
		int mid=(l+r+1)>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l<<'\n';
}

signed main() {
//	freopen("data.in","r",stdin);
//	freopen("myans.out","w",stdout);
//	ios::sync_with_stdio(false); 
//	cin.tie(0); cout.tie(0);
	double stt=clock();
	int _=1;
//	_=read();
//	cin>>_;
	For(i,1,_) {
		work();
	}
	cerr<<"\nTotal Time is:"<<(clock()-stt)*1.0/1000<<" second(s)."<<'\n';
	return 0;
}
posted @   coding_goat_qwq  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示