题干:

小A有一个含有n个非负整数的数列与m个区间,每个区间可以表示为li,ri。

它想选择其中k个区间, 使得这些区间的交的那些位置所对应的数的和最大。(是指k个区间共同的交,即每个区间都包含这一段,具体可以参照样例)

 

在样例中,5个位置对应的值分别为1,2,3,4,6,那么选择[2,5]与[4,5]两个区间的区间交为[4,5],它的值的和为10。

 收起

输入

第一行三个数n,k,m(1<=n<=100000,1<=k<=m<=100000)。
接下来一行n个数ai,表示小A的数列(0<=ai<=10^9)。
接下来m行,每行两个数li,ri,表示每个区间(1<=li<=ri<=n)。

输出

一行表示答案

输入样例

5 2 3
1 2 3 4 6
4 5
2 5
1 4

输出样例

10

解题报告:

   这题解法很多,因为元素值都是正数,所以我们可以枚举每一个位置,找到区间最左端的满足的,这一定就是对于当前i作为右端点的最大答案了。怎么看最左端满足的呢?也就是找到左端k个值,也就是找到左端第k大就行了,但是这题要判断一下是否一共这棵线段树中有k个元素,我们用tree[1].sum就可以得到。

  同样,维护位置和值 也可以用set。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
vector<int> vv[MAX];
int n,m,k; 
ll a[MAX];
struct TREE {
	int l,r;
	int sum;
} tree[MAX*4];
void pushup(int cur) {
	tree[cur].sum = tree[cur*2].sum + tree[cur*2+1].sum;
}
void build(int l,int r,int cur) {
	tree[cur].l = l;
	tree[cur].r = r;
	if(tree[cur].l == tree[cur].r) {
		tree[cur].sum = 0;return ;
	}
	int m = (l+r)>>1;
	build(l,m,cur*2);
	build(m+1,r,cur*2+1);
	pushup(cur);
}
int query(int tar,int cur) {
	if(tree[cur].l == tree[cur].r) return tree[cur].l;
	if(tar <= tree[cur*2].sum) return query(tar,cur*2);
	else return query(tar - tree[cur*2].sum,cur*2+1);
}
void update(int tar,int val,int cur) {
	if(tree[cur].l == tree[cur].r) {
		tree[cur].sum += val; return;
	}
	int m = tree[cur*2].r;
	if(tar <= m) update(tar,val,cur*2);
	else update(tar,val,cur*2+1); 
	pushup(cur);
}
int main() 
{
	ll ans=0;
	cin>>n>>k>>m;
	build(1,n,1);
	for(int i = 1; i<=n; i++) scanf("%lld",a+i),a[i] += a[i-1];
	for(int x,y,i = 1; i<=m; i++) {
		scanf("%d%d",&x,&y);
		vv[x].pb(x);
		vv[y+1].pb(-x);
	}
	for(int i = 1; i<=n; i++) {
		int up = vv[i].size();
		for(int j = 0; j<up; j++) {
			if(vv[i][j] > 0) update(vv[i][j],1,1);
			else update(-vv[i][j],-1,1);
		} 
		if(tree[1].sum >= k) {
			int pos = query(k,1);
			ans = max(ans,a[i] - a[pos-1]);
		} 
	}
	printf("%lld\n",ans);
	return 0;
}

首先排序右端点从小到大,然后枚举右端点(保证所枚举的那个端点最少有k个区间可以覆盖)作为所求的交区间的右端点,这时候需要求出交区间的左端点,我们可以知道,右端点确定下,如果左端点越靠左,这个区间的范围约大。为了保证所交区间有k个,我们需要找到第k小的左端点,为了保证我枚举的右端点肯定是交区间的右端点,我们必须边枚举,边单点更新左端点。
 

#include<bits/stdc++.h>
#define inf 1e18
#define ll long long
#define N 300010
#define lson rt<<1
#define rson rt<<1|1
#define mo 1000000007
using namespace std;
typedef pair<int,int> P;
inline ll read() {
	ll x=0,f=1;
	char ch=getchar();
	while (ch<'0'||ch>'9') {
		if (ch=='-') f=-1;
		ch=getchar();
	}
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int n,m,K;
ll sz[N*4],ans,sum[N];
struct node {
	int l,r;
} s[N];
bool cmp(node a,node b) {
	if (a.r==b.r) return a.l>b.l;
	return a.r<b.r;
}
void pushup(int rt) {
	sz[rt]=sz[lson]+sz[rson];
}
void modify(int rt,int l,int r,int pos) {
	if (l==r) {
		sz[rt]++;
		return ;
	}
	int mid=(l+r)>>1;
	if (pos<=mid) modify(lson,l,mid,pos);
	else modify(rson,mid+1,r,pos);
	pushup(rt);
}
int query(int rt,int l,int r,int x) {
	if (l==r) return l;
	int mid=(l+r)>>1;
	if (x<=sz[lson]) return query(lson,l,mid,x);
	else return query(rson,mid+1,r,x-sz[lson]);
}
int main() {
	n=read(),K=read(),m=read();
	for (int i=1; i<=n; i++) {
		sum[i]=read();
		sum[i]+=sum[i-1];
	}
	for (int i=1; i<=m; i++) s[i].l=read(),s[i].r=read();
	sort(s+1,s+m+1,cmp);
	for (int i=m-K+2; i<=m; i++) modify(1,1,n,s[i].l);
	for (int i=m-K+1; i>=1; i--) {
		modify(1,1,n,s[i].l);
		int x=query(1,1,n,K);
		if (x<=s[i].r) ans=max(ans,sum[s[i].r]-sum[x-1]);
	}
	printf("%lld",ans);
	return 0;
}

枚举i作为左端点的。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 100005;
ll sum[MAX] ;
ll a[MAX];
multiset<int> ss;
vector<int> vv[MAX];

int main() 
{
	int n,k,m;
	while(~scanf("%d%d%d",&n,&k,&m) ) {
		memset(sum,0,sizeof sum);
		ss.clear();
		for(int i = 1; i<=n; i++) vv[i].clear();
		for(int i = 1; i<=n; i++) scanf("%lld",&a[i]),sum[i] = sum[i-1] + a[i];
		while(m--) {
			int l,r;
			scanf("%d%d",&l,&r);
			vv[l].push_back(r);
		}
		ll ans = 0;
		for(int i = 1; i<=n; i++) {
			int up = vv[i].size();
			for(int j = 0 ; j<up ; j++) ss.insert(vv[i][j]);
			if(ss.empty() == 0) {
				while(ss.size() > k || *ss.begin() < i) {
					ss.erase(ss.begin());
					if(ss.size() == 0) break;
				}				
			}

				
			if(ss.size() == k) ans = max(ans , sum[*(ss.begin())] - sum[i-1]);
		}
		printf("%lld\n" , ans);
	}
	return 0;
}

 

 

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define fi first
#define se second
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<ll ,int> pii;
const ll N = 101000, siz = 20, mod = 1e9+7, blk = 316, eps = 1e-10;
ll fpow(ll a, ll b){ll ret=1;for(a%=mod;b;b>>=1,a=a*a%mod)if(b&1)ret=ret*a%mod;return ret;}
priority_queue<int, vector<ll >, greater<int> >q;
ll n, m, k, sum[N], ans, t;
pair<ll ,ll >a[N];
int main(){
    cin>>n>>k>>m;
    for(ll i=1;i<=n;i++){
        cin>>t;
        sum[i] = sum[i-1]+t;
    }
    for(ll i=0;i<m;i++)
        cin>>a[i].fi>>a[i].se;
    sort(a, a+m);
    for(ll i=0;i<m;i++){
        q.push(a[i].se);
        while(!q.empty() && q.top() < a[i].fi)
            q.pop();
        while(q.size() > k) q.pop();
        if(q.size() == k){
            //cout<<a[i].fi<<" "<<q.top()<<endl;
            ans = max(ans, sum[q.top()]-sum[a[i].fi-1]);
        }
    }
    cout<<ans<<endl;
    return 0;
}