【51Nod - 1672】【區間交】
阿新 • • 發佈:2018-11-01
題目:
小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;
}