1. 程式人生 > >NOIP2011聰明的質監員題解

NOIP2011聰明的質監員題解

輸入輸出 std under pri efault abs iostream using nbsp

631. [NOIP2011] 聰明的質監員

★★ 輸入文件:qc.in 輸出文件:qc.out 簡單對比
時間限制:1 s 內存限制:128 MB

【問題描述】
小 T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有n個礦石,從 1 到n逐一編號,每個礦石都有自己的重量wi以及價值vi。檢驗礦產的流程是:
1. 給定 m個區間[Li,Ri]
2. 選出一個參數W
3. 對於一個區間[Li,Ri],計算礦石在這個區間上的檢驗值Yi

Yi=j1×jvj, j[Li,Ri] wjW,j是礦石編號

這批礦產的檢驗結果Y為各個區間的檢驗值之和。即:

Y=i=1mYi

若這批礦產的

檢驗結果

與所給標準值 S 相差太多,就需要再去檢驗另一批礦產。小 T 不想費時間去檢驗另一批礦產,所以他想通過調整參數 W 的值,讓

檢驗結果

盡可能的靠近標準值 S,即使得

S?Y的絕對值最小。請你幫忙求出這個最小值。

【輸入】
輸入文件 qc.in。

第一行包含三個整數n,m,S,分別表示礦石的個數、區間的個數和標準值。
接下來的n 行,每行2 個整數,中間用空格隔開,第i+1 行表示i 號礦石的重量wi 和價值vi 。
接下來的m 行,表示區間,每行2 個整數,中間用空格隔開,第i+n+1 行表示區間[Li,Ri]的兩個端點Li 和Ri。註意:不同區間可能重合或相互重疊。

【輸出】
輸出文件名為qc.out。
輸出只有一行,包含一個整數,表示所求的最小值。

【輸入輸出樣例】

qc.in

5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3

qc.out

10

【輸入輸出樣例說明】
當W 選4 的時候,三個區間上檢驗值分別為20、5、0,這批礦產的檢驗結果為25,此時與標準值S 相差最小為10。
【數據範圍】
對於10%的數據,有1≤n,m≤10;
對於30%的數據,有1≤n,m≤500;
對於50%的數據,有1≤n,m≤5,000;
對於70%的數據,有1≤n,m≤10,000;
對於100%的數據,有1≤n,m≤200,000,0 < wi, vi≤10^6,0 < S≤10^12,1≤Li≤Ri≤n。

  這道題一開始看到Σ還以為是數學題,果斷跳過,然後發現自己錯了……

  然後又以為是平衡樹,然而昨天雖然剛打了平衡樹卻因為模板錯誤而沒能學習區間操作,果斷心虛。於是打了最暴力的暴力,強行水了5分。

  這道題最重要的有兩點,第一,前綴和,第二,二分。

  先解釋前綴和,這個很容易理解,就是sumv[i]為在i之前所有high不小於w的value的前綴和sumw[i]為在i之前高度不小於w的個數。

  然後就是二分,考試的時候以為是三分,之前沒打過,打掛了。如果以abs(Y-S)為y軸,w為x軸,那麽圖像是一個絕對值函數,類似二次函數,很容易讓人想到三分,然而值得註意的是Y本身是隨w增大而遞減的,而Y-S也是如此,我們就可以利用這個性質不斷地二分w,使Y無限的去接近S,Y大於0就右移,反之左移,記得開long long。

  

技術分享
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 #include<cmath>
 8 #include<map>
 9 using namespace std;
10 int n,m;
11 long long s;
12 struct no{
13     int va;
14     int w;
15 }node[2000005];
16 struct qu{
17     int li,ri;
18 }que[2000004];
19 long long sumw[2000005],sumv[2000005];
20 long long check(int w){
21     memset(sumv,0,sizeof(sumv));
22     memset(sumw,0,sizeof(sumw));
23     for(int i=1;i<=n;i++)
24     {
25         sumw[i]=sumw[i-1];
26         sumv[i]=sumv[i-1];
27         if(node[i].w>=w)
28         {
29             sumw[i]++;
30             sumv[i]+=node[i].va;
31         }
32     }
33     long long ans=0;
34     for(int i=1;i<=m;i++)
35     {
36         ans+=(sumv[que[i].ri]-sumv[que[i].li-1])*(sumw[que[i].ri]-sumw[que[i].li-1]);
37     }
38     return ans;
39 }
40 long long minn(long long a,long long b){
41     if(a>b)return b;
42     return a;
43 }
44 long long llab(long long a){
45     if(a<0)
46     return -a;
47     return a;
48 }
49 int main(){
50     scanf("%d%d%lld",&n,&m,&s);
51     int mx=0;
52     for(int i=1;i<=n;i++)
53     {
54         scanf("%d%d",&node[i].w,&node[i].va);
55         mx=max(mx,node[i].w);
56     }
57     for(int i=1;i<=m;i++)
58         scanf("%d%d",&que[i].li,&que[i].ri);
59     int li=1,ri=mx;
60     long long ans=-1;
61     while(li<=ri)
62     {
63         int mid=(li+ri)/2;
64         long long x=check(mid);
65         if(ans==-1)ans=llab(x-s);
66         ans=minn(ans,llab(x-s));
67         if(x>s)
68             li=mid+1;
69         else
70             ri=mid-1;
71     }
72     printf("%lld\n",ans);
73     //while(1);
74     return 0;
75 }
View Code

NOIP2011聰明的質監員題解