2 条题解

  • 0
    @ 2026-9-2 10:50:57

    思路

    f(W)=i=1myif(W)=\sum\limits_{i=1}^{m}y_i
    根据题意发现,随着 WW 增大,yiy_if(W)=i=1myif(W)=\sum\limits_{i=1}^{m}y_i 都单调不增。
    现在在单调函数 f(W)f(W) 上求最接近 ss 的值,容易想到二分 WW
    j=liri[wjW]\sum\limits_{j=l_i}^{r_i}[w_j \ge W] 指的是这个区间内有多少个矿石重量大于等于 WW
    j=liri[wjW]vj\sum\limits_{j=l_i}^{r_i}[w_j \ge W]v_j 指的是这个区间内重量大于等于 WW 的矿石价值之和。
    容易发现上面两个式子都可以 O(n)O(n) 预处理出前缀和,并用前缀和 O(1)O(1) 求出。

    最终做法就是二分 WW,每次二分都预处理两个前缀和,并利用前缀和分别求两个式子。

    时间复杂度为 O((n+m)logW)O((n+m)\log W)


    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m;
    ll s,ans=1e18;
    int w[200003],v[200003];
    int l[200003],r[200003];
    int cnt[200003];
    ll sumv[200003];
    ll check(int W){
    	memset(cnt,0,sizeof(cnt));
    	memset(sumv,0,sizeof(sumv));
    	for(int i=1;i<=n;i++){
    		cnt[i]=cnt[i-1];
    		sumv[i]=sumv[i-1];
    		if(w[i]>=W){
    			cnt[i]++;
    			sumv[i]+=v[i];
    		}
    	}
    	ll sum=0;
    	for(int i=1;i<=m;i++){
    		sum+=(cnt[r[i]]-cnt[l[i]-1])*(sumv[r[i]]-sumv[l[i]-1]);
    	}
    	return sum;
    }
    
    int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    	cin>>n>>m>>s;
    	int lft=0,rig=0,mid;
    	for(int i=1;i<=n;i++){
    		cin>>w[i]>>v[i];
    		rig=max(rig,w[i]);
    	}
    	for(int i=1;i<=m;i++){
    		cin>>l[i]>>r[i];
    	}
    	while(lft<=rig){
    		mid=lft+rig>>1;
    		ll y=check(mid);
    		ans=min(ans,abs(s-y));
    		if(y<s)rig=mid-1;
    		else lft=mid+1;
    	}
    	cout<<ans;
    	return 0;
    }
    
    • 0
      @ 2025-10-8 16:53:06
      #include <iostream>
      #include <cstring>
      #include <algorithm>
      using namespace std;
      
      typedef long long LL;
      const int N=200010;
      int n,m, w[N],v[N],l[N],r[N];
      LL s,sn[N],sv[N],ans=1e18;
      
      bool check(int W){    
        memset(sn,0,sizeof sn);
        memset(sv,0,sizeof sv);
        for(int i=1;i<=n;i++){ //前缀和
          if(w[i]>=W)sn[i]=sn[i-1]+1,sv[i]=sv[i-1]+v[i];
          else sn[i]=sn[i-1],sv[i]=sv[i-1];
        }
        LL y=0;
        for(int i=1;i<=m;i++)
          y+=(sn[r[i]]-sn[l[i]-1])*(sv[r[i]]-sv[l[i]-1]);
        ans=min(ans,llabs(y-s)); //最优解
        return y<=s; //W大,y小
      }
      LL find(){
        int l=0,r=1e6+1;
        while(l+1<r){
          int mid=l+r>>1;
          if(check(mid)) r=mid; //最小化
          else l=mid;
        }
        return ans;
      }
      int main(){
        scanf("%d %d %lld",&n,&m,&s); 
        for(int i=1;i<=n;i++)
          scanf("%d%d",&w[i],&v[i]);
        for(int i=1;i<=m;i++)
          scanf("%d%d",&l[i],&r[i]);
        printf("%lld",find());
        return 0;
      }
      
      • 1

      [NOIP 2011 提高组] 聪明的质监员

      信息

      ID
      67
      时间
      1000ms
      内存
      128MiB
      难度
      6
      标签
      递交数
      32
      已通过
      12
      上传者