BZOJ 3022 [Balkan2012]The Best Teams(掃描線+線段樹)
阿新 • • 發佈:2017-07-30
n) 最大 寫法 題目 -s 求解 std long 問題
那麽問題就轉化為在權值線段樹上求解k個不相鄰的位置,使得權值和最大,
我們維護g[0/1]數組表示r+1不選/選的時候,l位置選不選
c[0/1]數組表示r+1不選/選的時候,中間選了幾個。
s[0/1]數組表示r+1不選/選的時候,中間選的和。
查詢則類似權值線段樹上的k大數查詢。
學習了一下Claris的非遞歸線段樹寫法。
【題目鏈接】 http://www.lydsy.com/JudgeOnline/problem.php?id=3022
【題目大意】
給定n個球員,第i個球員年齡為AGEi,水平為SKILLi。
沒有任何兩個球員的水平相同。將這些球員按水平排序,
對於一次比賽,你需要選擇若幹個球員去比賽,但不能同時選擇兩個水平相鄰的球員。
m次詢問,每次給定a和k,表示要在年齡不超過a的球員中選擇不超過k個球員,
請計算skill和的最大值。
【題解】
對於詢問年齡的限制,我們可以通過掃描線來處理。
我們將所有人的水平映射到線段上,隨著線掃描在相應的位置更新上水平,
那麽問題就轉化為在權值線段樹上求解k個不相鄰的位置,使得權值和最大,
我們維護g[0/1]數組表示r+1不選/選的時候,l位置選不選
c[0/1]數組表示r+1不選/選的時候,中間選了幾個。
s[0/1]數組表示r+1不選/選的時候,中間選的和。
查詢則類似權值線段樹上的k大數查詢。
學習了一下Claris的非遞歸線段樹寫法。
【代碼】
#include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const int N=300010,M=N<<2; int n,m,disc[N]; LL ans[N]; struct E{int x,y,id;}a[N],b[N]; bool cmp(E a,E b){return a.x<b.x;} namespace Segment_Tree{ int pos[N]; struct data{bool g[2];int c[2];LL s[2];}T[M]; void build(int x,int l,int r){ if(l==r){pos[l]=x;return;} int mid=(l+r)>>1; build(x<<1,l,mid);build(x<<1|1,mid+1,r); } void change(int x,int y){ x=pos[x]; T[x].g[0]=T[x].c[0]=1,T[x].s[0]=y; for(x>>=1;x;x>>=1)for(int i=0;i<2;i++){ bool j=T[x<<1|1].g[i]; T[x].g[i]=T[x<<1].g[j]; T[x].c[i]=T[x<<1].c[j]+T[x<<1|1].c[i]; T[x].s[i]=T[x<<1].s[j]+T[x<<1|1].s[i]; } } LL ask(int k){ int x=1,l=1,r=n,u=0; LL res=0; while(k){ if(k>=T[x].c[u]){res+=T[x].s[u];break;} if(l==r)break; int mid=(l+r)>>1; x=x<<1|1; if(k<=T[x].c[u])l=mid+1; else{ k-=T[x].c[u]; res+=T[x].s[u]; u=T[x].g[u]; r=mid; x--; } }return res; } } int remark(int x){ int l=1,r=n; while(l<=r){ int mid=(l+r)>>1; if(disc[mid]<x)l=mid+1; else if(disc[mid]==x)return mid; else r=mid-1; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y),disc[i]=a[i].y; scanf("%d",&m); for(int i=1;i<=m;i++)scanf("%d%d",&b[i].x,&b[i].y),b[i].id=i; sort(a+1,a+n+1,cmp); sort(b+1,b+m+1,cmp); sort(disc+1,disc+n+1); Segment_Tree::build(1,1,n); for(int i=1,j=1;i<=m;i++){ while(j<=n&&a[j].x<=b[i].x)Segment_Tree::change(remark(a[j].y),a[j].y),j++; ans[b[i].id]=Segment_Tree::ask(b[i].y); }for(int i=1;i<=m;i++)printf("%lld\n",ans[i]); return 0; }
BZOJ 3022 [Balkan2012]The Best Teams(掃描線+線段樹)