loj2174 「FJOI2016」神秘数

先考虑一下一个集合怎么用 (O(n)) 时间求出来,然后用主席树推广到一个序列就可以了。大致思想就是考虑一个数的权值和它前面的数的和的关系。

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
int n, a[100005], cnt, b[100005], m, uu, vv, rot[100005], tot;
struct Node{
	int l, r, sum;
}nd[5000005];
int build(int l, int r){
	int re=++tot;
	if(l==r)	;
	else{
		int mid=(l+r)>>1;
		if(l<=mid)	nd[re].l = build(l, mid);
		if(mid<r)	nd[re].r = build(mid+1, r);
	}
	return re;
}
int update(int pre, int l, int r, int x){
	int re=++tot;
	nd[re] = nd[pre];
	nd[re].sum += b[x];
	if(l==r)	;
	else{
		int mid=(l+r)>>1;
		if(x<=mid)	nd[re].l = update(nd[pre].l, l, mid, x);
		if(mid<x)	nd[re].r = update(nd[pre].r, mid+1, r, x);
	}
	return re;
}
int query(int pre, int now, int l, int r, int x, int y){
	if(b[l]>=x && b[r]<=y)
		return nd[now].sum-nd[pre].sum;
	else if(l==r)
		return 0;
	else{
		int mid=(l+r)>>1;
		int re=0;
		if(x<=b[mid])	re += query(nd[pre].l, nd[now].l, l, mid, x, y);
		if(b[mid]<y)	re += query(nd[pre].r, nd[now].r, mid+1, r, x, y);
		return re;
	}
}
int getAns(int uu, int vv){
	int sum=0, lst=0;
	while(true){
		int tmp=query(rot[uu-1], rot[vv], 1, cnt, lst+1, sum+1);
		if(!tmp)
			return sum+1;
		lst = sum + 1;
		sum += tmp;
	}
}
int main(){
	cin>>n;
	for(int i=1; i<=n; i++){
		scanf("%d", &a[i]);
		b[i] = a[i];
	}
	sort(b+1, b+1+n);
	cnt = unique(b+1, b+1+n) - b - 1;
	for(int i=1; i<=n; i++)
		a[i] = lower_bound(b+1, b+1+cnt, a[i]) - b;
	rot[0] = build(1, cnt);
	for(int i=1; i<=n; i++)
		rot[i] = update(rot[i-1], 1, cnt, a[i]);
	cin>>m;
	while(m--){
		scanf("%d %d", &uu, &vv);
		printf("%d
", getAns(uu, vv));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/poorpool/p/9086913.html