1. 程式人生 > >【51Nod - 1672】【區間交】

【51Nod - 1672】【區間交】

題目:

小A有一個含有n個非負整數的數列與m個區間,每個區間可以表示為li,ri。

它想選擇其中k個區間, 使得這些區間的交的那些位置所對應的數的和最大。(是指k個區間共同的交,即每個區間都包含這一段,具體可以參照樣例)

 

在樣例中,5個位置對應的值分別為1,2,3,4,6,那麼選擇2,5與4,5兩個區間的區間交為4,5,它的值的和為10。

Input

第一行三個數n,k,m(1<=n<=100000,1<=k<=m<=100000)。 
接下來一行n個數ai,表示小A的數列(0<=ai<=10^9)。 
接下來m行,每行兩個數li,ri,表示每個區間(1<=li<=ri<=n)。

Output

一行表示答案

Sample Input

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

Sample Output

10

解題報告:貪心,之前剛接觸這道題目是完全沒有想法的,因為窮搜百分之百會超時,所以在掙扎之後,選擇了參考題解,見了一種新的stl容器  multiset 這個是set的特殊形式,特殊在他可以存放相同數值的元素(set中會自動抹去),之後就是貪心,每次將區間的右端點壓入multiset,(自動幫我們排為升序)和左端點進行比較,如果是左端點大於之前multiset中最小的右端點的話,抹去它,如果結束時候長度大於k 抹去前端。最後輸出這個區間的最大值。

ac程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;
typedef long long ll;

const int maxn=1e5+100;
ll sum[maxn];
struct node{
	int l,r;
}nod[maxn];
bool cmp(node a,node b)
{
	return a.l<b.l;
}

int main()
{
	int n,k,m;
	while(scanf("%d%d%d",&n,&k,&m)!=EOF)
	{
		int x;
		memset(sum,0,sizeof(sum));
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&x);
			sum[i]=sum[i-1]+x;
		}
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&nod[i].l,&nod[i].r);
		}
		sort(nod+1,nod+1+m,cmp);
		multiset<int > s;
		ll ans=0;
		for(int i=1;i<=m;i++)
		{
			int l=nod[i].l,r=nod[i].r;
			s.insert(r);
			while(s.size())
			{
				if(*s.begin()<l) s.erase(s.begin());
				else break;
			}
			while(s.size()>k)
			{
				s.erase(s.begin());
			}
			if(s.size()==k)
			{
				ans=max(ans,sum[*s.begin()]-sum[l-1]);
			}
		}
		printf("%lld\n",ans);
	}	
	return 0;
} 

上邊是利用stl的貪心解法,下面這個是線段樹求解,按照將麼個區間左端點作為所找區間的左端點,看看他的右端點將會跑到哪,進行線段樹求解,尋找出最大值。(= =) (線段樹遺忘的很多,打算補後再細分析)

ac程式碼:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn=1e5+100;
int num[maxn<<2];
ll sum[maxn<<1];

struct node{
	friend bool operator < (const node &a,const node &b){
		return a.l<b.l;
	}
	int l,r;
}nod[maxn];

void updata(int n,int l,int r,int h)
{
	if(l==r)
	{
		num[n]++;
		return ;
	}
	int mid=(l+r)>>1;
	if(h<=mid)
		updata(n<<1,l,mid,h);
	else
		updata(n<<1|1,mid+1,r,h);
	num[n]=num[n<<1]+num[n<<1|1];	
}
int query(int n,int l,int r,int k)
{
	if(l==r)
		return l;
	int mid=(l+r)>>1;
	if(num[n<<1|1]>=k)
		return query(n<<1|1,mid+1,r,k);
	else
		return query(n<<1,l,mid,k-num[n<<1|1]);
}

int main()
{
	int n,k,m;
	scanf("%d%d%d",&n,&k,&m);
	for(int i=1;i<=n;i++)
		scanf("%lld",&sum[i]),sum[i]+=sum[i-1];
	for(int i=1;i<=m;i++)
		scanf("%d%d",&nod[i].l,&nod[i].r);
	sort(nod+1,nod+1+m);
	for(int i=1;i<=k;i++)
	{
		updata(1,1,n,nod[i].r);
	}
	ll ans=0;
	for(int i=k;i<=m;i++)
	{
		int s=query(1,1,n,k);
		if(s>=nod[i].l&&s<=nod[i].r)
			ans=max(ans,sum[s]-sum[nod[i].l-1]);
		if(i!=m)
			updata(1,1,n,nod[i+1].r);
	}
	printf("%lld\n",ans);
	return 0;
}