首页 > 其他分享 >P2839 [国家集训队] middle(二分+可持久化线段树)

P2839 [国家集训队] middle(二分+可持久化线段树)

时间:2024-10-24 20:31:37浏览次数:8  
标签:std P2839 int 线段 mid middle qry 国家集训队 define

P2839 [国家集训队] middle

二分+可持久化线段树

中位数经典做法,二分答案,将小于的部分看做 \(-1\),大于等于的部分看做 \(+1\),那么答案可以更大的条件就是区间和大于等于 \(0\)(等于 \(0\) 可不可以取到看是下取整还是上取整,本题是上取整)。

那么问题就是怎么判断有没有这样一个区间满足条件了。可以想到取 \([a,b]\) 中一段最大后缀子段和,取 \([b+1,c-1]\),取 \([c,d]\) 的一段最大前缀子段和,和最大。

想到这里,如果每一次二分都要建一棵新的树,那么复杂度就炸了。这里可以发现每一个数从 \(+1\) 变为 \(-1\) 只改变了一条线段树上的链,所以可以用可持久化线段树预处理出每个数的线段树。

复杂度 \(O(n\log^2 n)\)。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define mk std::make_pair
#define pb push_back

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e4 + 10;
int n, Q, ans, tot;
int q[4], a[N], id[N], rt[N];
struct seg {
	int l, r;
	int lmax, rmax, sum;
} t[N << 5];
void pushup(seg &u, seg ls, seg rs) {
	u.lmax = std::max(ls.lmax, ls.sum + rs.lmax);
	u.rmax = std::max(rs.rmax, rs.sum + ls.rmax);
	u.sum = ls.sum + rs.sum;
}
void build(int &u, int l, int r) {
	u = ++tot;
	if(l == r) {t[u].sum = t[u].lmax = t[u].rmax = 1; return;}
	int mid = (l + r) >> 1;
	build(t[u].l, l, mid), build(t[u].r, mid + 1, r);
	pushup(t[u], t[t[u].l], t[t[u].r]);
}
void upd(int &o, int u, int l, int r, int x) {
	o = ++tot;
	t[o] = t[u];
	if(l == r) {
		t[o].sum = t[o].lmax = t[o].rmax = -1;
		return;
	}
	int mid = (l + r) >> 1;
	if(x <= mid) upd(t[o].l, t[u].l, l, mid, x);
	else upd(t[o].r, t[u].r, mid + 1, r, x);
	pushup(t[o], t[t[o].l], t[t[o].r]);
}
seg qry(int u, int l, int r, int L, int R) {
	if(L <= l && r <= R) return t[u];
	int mid = (l + r) >> 1;
	if(R <= mid) return qry(t[u].l, l, mid, L, R);
	if(L > mid) return qry(t[u].r, mid + 1, r, L, R);
	seg ret = {0, 0, 0}, ls = qry(t[u].l, l, mid, L, R), rs = qry(t[u].r, mid + 1, r, L, R);
	pushup(ret, ls, rs);
	return ret;
}
bool check(int x) {
	seg f1 = qry(rt[x], 1, n, q[0], q[1]), f3 = qry(rt[x], 1, n, q[2], q[3]);
	if(q[1] + 1 <= q[2] - 1) {
		seg f2 = qry(rt[x], 1, n, q[1] + 1, q[2] - 1);
		// std::cout << x << " " << f1.rmax << " " << f2.sum << " " << f3.lmax << " f\n";
		return f1.rmax + f2.sum + f3.lmax >= 0;
	}
	// std::cout << x << " " << f1.rmax << " " << f3.lmax << " f\n";
 	return f1.rmax + f3.lmax >= 0; 
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	
	std::cin >> n;
	for(int i = 1; i <= n; i++) std::cin >> a[i], id[i] = i;
	std::sort(id + 1, id + n + 1, [](int x, int y) {return a[x] < a[y];});
	build(rt[1], 1, n);
	for(int i = 2; i <= n; i++) {
		upd(rt[i], rt[i - 1], 1, n, id[i - 1]);
	}
	std::cin >> Q;
	int lst = 0;
	while(Q--) {
		for(int i = 0; i < 4; i++) std::cin >> q[i], q[i] = (q[i] + lst) % n + 1;
		std::sort(q, q + 4);
		// std::cout << q[0] << " " << q[1] << " " << q[2] << " " << q[3] << "\n";
		int l = 1, r = n;
		while(l <= r) {
			int mid = (l + r) >> 1;
			if(check(mid)) ans = a[id[mid]], l = mid + 1;
			else r = mid - 1;
		}
		std::cout << ans << "\n";
		lst = ans;
	}

	return 0;
}

标签:std,P2839,int,线段,mid,middle,qry,国家集训队,define
From: https://www.cnblogs.com/FireRaku/p/18500438

相关文章

  • leetcode 876. Middle of the Linked List
    leetcode876.MiddleoftheLinkedList不容易出错的写法,慢classSolution{public:ListNode*middleNode(ListNode*head){if(!head||!head->next){returnhead;}ListNode*single=head,*double_=head;int......
  • Meet in the middle
    Meetinthemiddle双端搜索不是怎么这个人现在才会双端搜索Meetinthemiddle,顾名思义,就是从两端进行搜索,然后把两端的答案合并得到最终答案。如果原本的搜索时间复杂度为\(O(a^b)\),那么Meetinthemiddle可以将搜索的时间复杂度优化到\(O(wa^{\frac{b}{2}})\),其中\(......
  • FastAPI 应用安全加固:HTTPSRedirectMiddleware 中间件全解析
    在当今的网络环境中,数据安全变得越来越重要。HTTPS作为一种安全协议,它通过加密传输数据来保护用户信息免受窃取和篡改。在FastAPI应用中,确保所有的HTTP请求都通过HTTPS进行是至关重要的。中间件在FastAPI中用于处理请求前后的通用任务,例如身份验证、日志记录、请......
  • [国家集训队] Crash的数字表格 / JZPTAB
    [国家集训队]Crash的数字表格/JZPTAB题目描述今天的数学课上,Crash小朋友学习了最小公倍数(LeastCommonMultiple)。对于两个正整数\(a\)和\(b\),\(\text{lcm}(a,b)\)表示能同时被\(a\)和\(b\)整除的最小正整数。例如,\(\text{lcm}(6,8)=24\)。回到家后,Crash还在想......
  • 题解 P4827【[国家集训队] Crash 的文明世界】
    从阶乘幂到斯特林数-caijianhong-博客园(cnblogs.com)题目描述Crash小朋友最近迷上了一款游戏——文明5(CivilizationV)。在这个游戏中,玩家可以建立和发展自己的国家,通过外交和别的国家交流,或是通过战争征服别的国家。现在Crash已经拥有了一个\(n\)个城市的国家,这......
  • fastapi middleware中间件
    一、介绍FastAPI中的中间件(Middleware)是一个非常重要的概念,它允许开发者在请求被处理之前和响应被发送之前执行自定义逻辑。中间件在Web应用程序中扮演着桥梁的角色,连接着客户端的请求和服务器端的响应处理过程。以下是FastAPI中间件概念的详细解释:1.中间件的定义在FastAPI中,......
  • 折半搜素(meet in the middle)
    算法介绍折半搜素通常用来处理数据规模不能直接通过暴力解决,但数据规模又没有特别大的情况。例如:[P10484送礼物](P10484送礼物-洛谷|计算机科学教育新生态(luogu.com.cn))题意:作为惩罚,GY被遣送去帮助某神牛给女生送礼物(GY:貌似是个好差事)但是在GY看到礼物之后,他就不......
  • 搜索之meet in middle(有效的小方法)
    题目:[https://www.luogu.com.cn/problem/P2962](P2962[USACO09NOV]LightsG)算法:meetinmiddle(折半搜索)思路:有\(35\)个点,总共的操作状态有\(2^{35}\)种情况。如果我们采用一般的搜索方式,时间上会毫不犹豫得爆掉。所以,我们要用折半搜索的方式。将所有的点拆分成两个集合,对......
  • 使用 addRouteMiddleware 动态添加中间
    title:使用addRouteMiddleware动态添加中间date:2024/8/4updated:2024/8/4author:cmdragonexcerpt:摘要:文章介绍了Nuxt3中addRouteMiddleware的使用方法,该功能允许开发者动态添加路由中间件,以实现诸如权限检查、动态重定向及路由变化时的特定操作。内容涵盖路由中间......
  • FastAPI Starlette Middleware 会话 - 重定向后无法访问会话数据
    我正在使用FastAPI作为后端编写一个简单的Web应用程序,并且我希望通过AzureB2C实现身份验证。这一切的逻辑现在并不重要,我只是想能够测试一下我是否可以使用不同的方法成功登录。但是,在尝试了很长一段时间之后,我不明白,为什么我可以重定向后不会从会话中检索用户的数据......