聪明的质监员
题目链接:https://www.luogu.org/problemnew/show/P1314
Y(W)随W的值增大而减小
二分W的值,找到最小的W使得Y(W)>S;
比较Y(W)和Y(W-1)与S的差值。
计算Y(W):
O(n)预处理一维前缀和数组,
O(m)暴力计算出Y(W)
1 #include<cstdio> 2 using namespace std; 3 long long n,m,S,ranges[200010][2],w[200010],v[200010]; 4 inline long long abs(long long x) 5 { 6 if(x<0) x=-x; 7 return x; 8 } 9 inline long long min(long long a,long long b) 10 { 11 return a<b?a:b; 12 } 13 long long sum1[200010],sum2[200010]; 14 long long Y(long long x) //计算Y(W) 15 { 16 long long ans=0; 17 sum1[0]=0;sum2[0]=0; 18 for(int i=1;i<=n;i++) 19 if(w[i]>x) { 20 sum1[i]=sum1[i-1]+1; //前缀和数组sum1记录个数和 21 sum2[i]=sum2[i-1]+v[i]; //sum2记录价值和 22 } 23 else { 24 sum1[i]=sum1[i-1]; 25 sum2[i]=sum2[i-1]; 26 } 27 for(int i=1;i<=m;i++) 28 { 29 int l=ranges[i][0],r=ranges[i][1]; 30 ans+=(sum1[r]-sum1[l-1])*(sum2[r]-sum2[l-1]); 31 } 32 return ans; 33 } 34 int main() 35 { 36 int l=1,r=0; 37 scanf("%lld%lld%lld",&n,&m,&S); 38 for(int i=1;i<=n;i++) 39 { 40 scanf("%lld%lld",&w[i],&v[i]); 41 if(w[i]>r) r=w[i]; 42 } 43 for(int i=1;i<=m;i++) 44 scanf("%lld%lld",&ranges[i][0],&ranges[i][1]); 45 while(l<r) //二分W 46 { 47 int mid=(l+r)>>1; 48 if(Y(mid)>=S) l=mid+1; 49 else r=mid-1; 50 } 51 if(Y(l)<S) l-=1; 52 printf("%lld\n",min(abs(Y(l)-S),abs(Y(l+1)-S))); 53 return 0; 54 }