BZOJ1071 [SCOI2007]壓縮 指針
阿新 • • 發佈:2017-08-24
clu ont 子序列 ron int 我們 博客園 最長 sco
於是我們可以按照兩種順序排序,一個是v,一個是b。
那麽如果確定鼓勵va,則:
A*a[i]+B*b[i]<=A*va+B*vb+C
而A*a[i]>=A*va
所以B*b[i]<=B*vb+C
移項B*(b[i]-vb)<=C,
b[i]<=vb+C/B,
都是整數,所以可以直接取整。
所以,對於所有的b[i],有vb<=b[i]<=vb+C/B。
然後雙指針,一個掃按照b排序的,一個掃按照s排序的。
歡迎訪問~原文出處——博客園-zhouzhendong
去博客園看該題解
題目傳送門 - BZOJ1071
題意概括
有兩個序列a[1..n], b[1..n],其編號為1..n,設為s序列。現在我們要求出最長的滿足條件的s的子序列s‘,設va=min(a[s[i]]), vb=min(b[s[i]]), 滿足對於所有的j=s‘[i], A*(a[j]-va)+B*(b[j]-vb)<=C。
題解
設v[i]=A*a[i]+B*b[i];
那麽,要求滿足v[s‘[i]]-A*va-B*vb<=C,
移項得:v[s[i]]<=A*va+B*vb+C
於是我們可以按照兩種順序排序,一個是v,一個是b。
那麽如果確定鼓勵va,則:
A*a[i]+B*b[i]<=A*va+B*vb+C
而A*a[i]>=A*va
所以B*b[i]<=B*vb+C
移項B*(b[i]-vb)<=C,
b[i]<=vb+C/B,
都是整數,所以可以直接取整。
所以,對於所有的b[i],有vb<=b[i]<=vb+C/B。
然後雙指針,一個掃按照b排序的,一個掃按照s排序的。
代碼
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; typedef long long LL; const int N=5000+5; struct Player{ LL a,b,v; }v[N],a[N]; int n; LL A,B,C; bool cmp_v(Player a,Player b){ return a.v<b.v; } bool cmp_a(Player a,Player b){ return a.a<b.a; } int main(){ scanf("%d%lld%lld%lld",&n,&A,&B,&C); for (int i=1;i<=n;i++){ scanf("%lld%lld",&v[i].a,&v[i].b); v[i].v=A*v[i].a+B*v[i].b; a[i]=v[i]; } sort(v+1,v+n+1,cmp_v); sort(a+1,a+n+1,cmp_a); int ans=0; for (int i=1;i<=n;i++){ int L=0,R=0,cnt=0; LL xL=a[i].b,xR=xL+C/B; LL va,vb=xL; for (int j=1;j<=n;j++){ va=a[j].a; while (R<n&&v[R+1].v<=A*va+B*vb+C){ R++; if (xL<=v[R].b&&v[R].b<=xR) cnt++; } while (L<n&&a[L+1].a<va){ L++; if (xL<=a[L].b&&a[L].b<=xR) cnt--; } ans=max(ans,cnt); } } printf("%d",ans); return 0; }
BZOJ1071 [SCOI2007]壓縮 指針