zoukankan      html  css  js  c++  java
  • 【51nod1672】区间交

    题目大意:给定一个长度为 N 的序列,以及 M 个区间,现从中选出 K 个区间,使得这些区间的交集区间的点权和最大,求最大值是多少。

    题解:
    发现直接选 K 个区间不可做,考虑从答案入手。设答案区间为 [l,r],进行枚举答案区间的左端点。当前枚举到的左端点设为 L,那么能够以 L 作为左端点的区间一定满足左端点不超过 L,且右端点大于等于 L。考虑若有超过 K 个区间符合要求,那么肯定是选取较大的 K 个区间的答案更优,因此只需求出符合条件的区间右端点的第 K 大值,并更新答案即可。再考虑 L 之间的转移带来的变化,枚举到 L 时,应该将符合要求的答案更新;同样,统计完 L 的贡献之后,应该将对于 L+1 不符合情况的解删去。需要维护一个支持插入删除和求第 K 大的数据结构,显然权值线段树符合要求。

    代码如下

    #include <bits/stdc++.h>
    #define pb push_back
    using namespace std;
    const int maxn=1e5+10;
    typedef long long LL;
    
    int n,m,K,sz[maxn<<2];
    LL a[maxn],sum[maxn],ans;
    vector<int> st[maxn],ed[maxn];
    void insert(int o,int l,int r,int pos,int val){
    	if(l==r){sz[o]+=val;return;}
    	int mid=l+r>>1;
    	if(pos<=mid)insert(o<<1,l,mid,pos,val);
    	else insert(o<<1|1,mid+1,r,pos,val);
    	sz[o]=sz[o<<1]+sz[o<<1|1];
    }
    int kth(int o,int l,int r,int k){
    	if(l==r)return l;
    	int mid=l+r>>1;
    	if(sz[o<<1|1]>=k)return kth(o<<1|1,mid+1,r,k);
    	else return kth(o<<1,l,mid,k-sz[o<<1|1]);
    }
    
    void read_and_parse(){
    	scanf("%d%d%d",&n,&K,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%lld",&a[i]);
    		sum[i]=sum[i-1]+a[i];
    	}
    	for(int i=1;i<=m;i++){
    		int l,r;
    		scanf("%d%d",&l,&r);
    		st[l].pb(r),ed[r].pb(r);
    	}
    }
    void solve(){
    	for(int i=1;i<=n;i++){
    		for(auto r:st[i])insert(1,1,n,r,1);
    		if(sz[1]>=K){
    			int pos=kth(1,1,n,K);
    			ans=max(ans,sum[pos]-sum[i-1]);
    		}
    		for(auto r:ed[i])insert(1,1,n,r,-1);
    	}
    	printf("%lld
    ",ans);
    }
    int main(){
    	read_and_parse();
    	solve();
    	return 0;	
    } 
    
  • 相关阅读:
    Google笔试题
    OpenStack Ceilometer简介
    OpenStack Object Storage(Swift)架构、原理及特性
    【大话存储2读书笔记】磁盘IO的重要概念
    递归,汉诺塔游戏
    函数定义与使用
    字符串操作
    for循环:用turtle画一颗五角星
    列表、元组、字典、集合的定义、操作与综合练习
    npm 相关
  • 原文地址:https://www.cnblogs.com/wzj-xhjbk/p/11074078.html
Copyright © 2011-2022 走看看