毒瘤(指题目名)

毒瘤(指题目名)_第1张图片

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

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

所以我们考虑换一个思路

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

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

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

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

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

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

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

#include 
using namespace std;
typedef long long lnt;
typedef long long ll;
typedef pair 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  v;
set  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::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;
}

 

你可能感兴趣的:(线段树,并查集,思路题)