毒瘤(指题目名)

5 篇文章 0 订阅
2 篇文章 0 订阅

首先如果我们不考虑复杂度的话,直接主席树二分就能轻松解决了

但是3e5的数据范围会让nlog^n的算法跑得很慢,而且常数也比较大

所以我们考虑换一个思路

假如我们从小到大把点按权值排序,然后开始用并查集合并,记录下合并时的位置的值,最左边的位置,以及所在块的权值

接着我们考虑把询问的权值从大到小排序,那么对于每一个询问,就可以把所有权值比他大的合并点都加进来

我们设询问的左端点l作为左边界时,右边界最小为minpos,右端点r作为右边界时,最大的左边界为maxpos;

这两个可以先统计一边贡献,对于之前的每次合并,我们在最左边的位置上修改最小值为合并时位置的值

那么我们查询时只需要查询l到maxpos所对应的最小值就可以了,

可以证明如果多了肯定不优,所以一定正确

别人的代码,我是真的不想写了

#include <bits/stdc++.h>
using namespace std;
typedef long long lnt;
typedef long long ll;
typedef pair<int,int> pii;
const lnt inf  = 2e18;
const int N = 3e5 + 10;
int read() {
	int x = 0;
	char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = (x << 1) + (x << 3) + c - '0' , c = getchar();
	return x;
}
lnt readll() {
	lnt x = 0;
	char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = (x << 1) + (x << 3) + c - '0' , c = getchar();
	return x;
}
void writeln(int x) {
	if (!x) {
		puts("0");
		return;
	}
	int dg[20] , len = 0;

	if (x < 0) x = -x , putchar('-');

	while (x) {
		dg[len++] = x % 10;
		x /= 10;
	}

	while (len--) {
		putchar(dg[len] + '0');
	}
	putchar('\n');
}
struct interval {
	int l , r , maxn;
	lnt sum;
	inline bool operator < (const interval &A) const {
		return sum > A.sum;
	}
};
int n , m , d[N] , rnk[N];
lnt sum[N] , minm[N << 1] , maxn[N << 1];
lnt calc(int l,int r) {
	return (sum[r] - sum[l-1]) * (r - l + 1);
}
void modify(int x,int l,int r,int pos,int v) {
	if (l == r) {
		minm[x] = v;
		return;
	}
	int m = l + r >> 1 , z = x + ((m - l + 1) << 1);
	if (pos <= m) {
		modify(x + 1 , l , m , pos , v);
	} else {
		modify(z , m + 1 , r , pos , v);
	}
	minm[x] = min(minm[x + 1] , minm[z]);
}
int query(int x,int l,int r,int ql,int qr) {
	if (ql <= l && r <= qr) {
		return minm[x];
	}
	int m = l + r >> 1 , z = x + ((m - l + 1) << 1) , res = 1000000000;
	if (ql <= m) res = min(res , query(x + 1 , l , m , ql , qr));
	if (m < qr ) res = min(res , query(z , m + 1 , r , ql , qr));
	return res;
}
void build(int x,int l,int r) {
	minm[x] = 1000000000;
	if (l == r) {
		maxn[x] = d[l];
		return;
	}
	int m = l + r >> 1 , z = x + ((m - l + 1) << 1);
	build(x + 1 , l , m);
	build(z , m + 1 , r);
	maxn[x] = max(maxn[x + 1] , maxn[z]);
}
int querymax(int x,int l,int r,int ql,int qr) {
	if (ql <= l && r <= qr) {
		return maxn[x];
	}
	int m = l + r >> 1 , z = x + ((m - l + 1) << 1) , res = 0;
	if (ql <= m) res = max(res , querymax(x + 1 , l , m , ql , qr));
	if (m < qr ) res = max(res , querymax(z , m + 1 , r , ql , qr));
	return res;
}
vector <interval> v;
set <int> s;
struct Asktion {
	int l , r , id;
	lnt x;
	inline bool operator < (const Asktion &A) const {
		return x > A.x;
	}
};
Asktion asks[N];
int res[N];
int getposl(int l,lnt x) {
	int L = l , R = n  , res = -1;
	while (L <= R) {
		int mid = L + R >> 1;
		if (calc(l , mid) >= x) {
			res = mid;
			R = mid - 1;
		} else {
			L = mid + 1;
		}
	}
	return res;
}
int getposr(int r,lnt x) {
	int L = 1 , R = r  , res = -1;
	while (L <= R) {
		int mid = L + R >> 1;
		if (calc(mid , r) >= x) {
			res = mid;
			L = mid + 1;
		} else {
			R = mid - 1;
		}
	}
	return res;
}
int main() {
	scanf("%d%d",&n,&m);
	for (int i = 1; i <= n; i++) {
		d[i] = read();
		rnk[i] = i;
		sum[i] = sum[i-1] + d[i];
	}
	build(1 , 1 , n);
	sort(rnk + 1 , rnk + 1 + n , [](int a,int b) -> bool {return d[a] > d[b];});
	s.insert(0);
	s.insert(n + 1);
	for (int i = 1; i <= n; i++) {
		s.insert(rnk[i]);
		set<int>::iterator it = s.find(rnk[i]);
		int pre = *(--it) + 1 , nxt = *(++++it) - 1;
		v.push_back((interval) {
			pre , nxt , d[rnk[i]] , 1ll * (nxt - pre + 1) * (sum[nxt] - sum[pre-1])
		});

	}
	sort(v.begin() , v.end());
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%lld",&asks[i].l,&asks[i].r,&asks[i].x);
		asks[i].x = (asks[i].x + 1) >> 1;
		asks[i].id = i;
	}
	sort(asks + 1 , asks + 1 + m);
	int now = -1;
	for (int i = 1; i <= m; i++) {
		while (now + 1 < (int) v.size() && v[now + 1].sum  >= asks[i].x) {
			now++;
			modify(1 , 1 , n , v[now].l , v[now].maxn);
		}
		int l = asks[i].l , r = asks[i].r , L = getposl(l , asks[i].x) , R = getposr(r , asks[i].x);

		if (L == -1 || L > r) {
			res[asks[i].id] = -1;
			continue;
		}
		res[asks[i].id] = min( query(1 , 1 , n , l , R) , min(querymax(1 , 1 , n , l , L) , querymax(1 , 1 , n , R , r) ) );
	}
	for (int i = 1; i <= m; i++) printf("%d ",res[i]);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值