NOIP2011 聪明的质检员
题目描述的意思就是,对于给定的所有区间,计算每个区间里面有多少个质量大于w的矿石,把个数乘以价值和即为区间贡献,设定一个w值使得区间贡献和最接近S。
可以看出,这个贡献和肯定是随着w具有单调性变化的,我们又想到二分答案……然后每次更新一下差值最小值,如果贡献和大于S就增大w,否则减小w。
计算的时候使用前缀和既可以O(n)计算出答案,总复杂度O(nlogw)
看一下代码。
// luogu-judger-enable-o2 #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 200005; const ll INF = 1e17+9; const ll mod = 1e9+7; ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct seg { ll l,r; }b[M]; ll a[M],n,m,S,w[M],v[M],minn = INF,sum[M],vsum[M],L,R; ll calc(ll x) { ll p = 0; memset(sum,0,sizeof(sum)); memset(vsum,0,sizeof(vsum)); rep(i,1,n) { sum[i] = sum[i-1],vsum[i] = vsum[i-1]; if(w[i] >= x) sum[i]++,vsum[i] += v[i]; } rep(i,1,m) p += (sum[b[i].r] - sum[b[i].l-1]) * (vsum[b[i].r] - vsum[b[i].l-1]); return p; } int main() { n = read(),m = read(),S = read(),L = 0; rep(i,1,n) w[i] = read(),v[i] = read(),R = max(R,w[i]); rep(i,1,m) b[i].l = read(),b[i].r = read(); while(L <= R) { ll mid = (L + R) >> 1,k = calc(mid); minn = min(minn,abs(k-S)); if(k > S) L = mid + 1; else R = mid - 1; } printf("%lld\n",minn); return 0; }
当你意识到,每个上一秒都成为永恒。