1. 程式人生 > >NOIP2012借教室

NOIP2012借教室

nod log style set 正整數 現在 數據 pan scrip

題目描述 Description

在大學期間,經常需要租借教室。大到院系舉辦活動,小到學習小組自習討論,都需要

向學校申請借教室。教室的大小功能不同,借教室人的身份不同,借教室的手續也不一樣。

面對海量租借教室的信息,我們自然希望編程解決這個問題。

我們需要處理接下來n天的借教室信息,其中第i天學校有ri個教室可供租借。共有m份

訂單,每份訂單用三個正整數描述,分別為dj, sj, tj,表示某租借者需要從第sj天到第tj天租

借教室(包括第sj天和第tj天),每天需要租借dj個教室。

我們假定,租借者對教室的大小、地點沒有要求。即對於每份訂單,我們只需要每天提

供dj個教室,而它們具體是哪些教室,每天是否是相同的教室則不用考慮。

借教室的原則是先到先得,也就是說我們要按照訂單的先後順序依次為每份訂單分配教

室。如果在分配的過程中遇到一份訂單無法完全滿足,則需要停止教室的分配,通知當前申

請人修改訂單。這裏的無法滿足指從第sj天到第tj天中有至少一天剩余的教室數量不足dj個。

現在我們需要知道,是否會有訂單無法完全滿足。如果有,需要通知哪一個申請人修改

訂單。

輸入描述 Input Description

第一行包含兩個正整數n, m,表示天數和訂單的數量。

提高組 day2

第二行包含n個正整數,其中第i個數為ri,表示第i天可用於租借的教室數量。

接下來有m行,每行包含三個正整數dj, sj, tj,表示租借的數量,租借開始、結束分別在

第幾天。

每行相鄰的兩個數之間均用一個空格隔開。天數與訂單均用從1開始的整數編號。

輸出描述 Output Description

如果所有訂單均可滿足,則輸出只有一行,包含一個整數 0。否則(訂單無法完全滿足)

輸出兩行,第一行輸出一個負整數-1,第二行輸出需要修改訂單的申請人編號。

樣例輸入 Sample Input

4 3

2 5 4 3

2 1 3

3 2 4

4 2 4

樣例輸出 Sample Output

-1

2

數據範圍及提示 Data Size & Hint

【輸入輸出樣例說明】

classroom.out

-1

2

第 1 份訂單滿足後,4 天剩余的教室數分別為 0,3,2,3。第 2 份訂單要求第 2 天到

第 4 天每天提供 3 個教室,而第 3 天剩余的教室數為 2,因此無法滿足。分配停止,通知第

2 個申請人修改訂單。

【數據範圍】

對於 10%的數據,有1 ≤ n, m ≤ 10;

對於 30%的數據,有1 ≤ n, m ≤ 1000;

對於 70%的數據,有1 ≤ n, m ≤ 105;

對於 100%的數據,有1 ≤ n, m ≤ 10^6, 0 ≤ ri, dj≤ 10^9, 1 ≤ sj≤ tj≤ n。、

最開始看到這道題的時候是在刷線段樹專題的時候,當時老師說借教室這道題可以用線段樹水過,下面貼上寫的非常醜的線段樹做法

 1 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4 int h[1000010];
 5 struct node
 6 {
 7     int l,r,min,sign;
 8 }tree[4000000];
 9 void build(int now,int l,int r)
10 {
11     int mid=(l+r)/2;
12     tree[now].l=l;
13     tree[now].r=r;
14     if(l==r)
15     {
16         tree[now].min=h[l];
17         return;
18     }
19     build(now*2,l,mid);
20     build(now*2+1,mid+1,r);
21     tree[now].min=min(tree[now*2].min,tree[now*2+1].min);
22 }
23 void putdown(int now)
24 {
25     tree[now*2].sign+=tree[now].sign;
26     tree[now*2+1].sign+=tree[now].sign;
27     tree[now*2].min-=tree[now].sign;
28     tree[now*2+1].min-=tree[now].sign;
29     tree[now].sign=0;
30 }
31 bool gai(int now,int l,int r,int sum)
32 {
33     if(tree[now].l>r||tree[now].r<l) return true;
34     if(tree[now].l>=l&&tree[now].r<=r)
35     {
36         tree[now].min-=sum;
37         tree[now].sign+=sum;
38         if(tree[now].min<0) return false;
39         return true;
40     }
41     if(tree[now].l!=tree[now].r&&tree[now].sign!=0) putdown(now);
42     if((gai(now*2,l,r,sum)==0)||(gai(now*2+1,l,r,sum)==0)) 
43     {
44         tree[now].min=min(tree[now*2].min,tree[now*2+1].min);
45         return false;
46     }
47     tree[now].min=min(tree[now*2].min,tree[now*2+1].min);
48     return true;
49 }
50 int main()
51 {
52     int i,j,dj,sj,tj,n,m;
53     scanf("%d %d",&n,&m);
54     for(i=1;i<=n;i++)
55       scanf("%d",&h[i]);
56     build(1,1,n);
57     for(j=1;j<=m;j++)
58     {
59         scanf("%d %d %d",&dj,&sj,&tj);
60         if(gai(1,sj,tj,dj)==0)
61         {
62             printf("-1\n%d",j);
63             return 0;
64         }
65     }
66     printf("0");
67     return 0;
68 }

現在看看當初寫的線段樹真是不能再暴力了。

下面說正解,這道題是求最小的天數,使所有人都可以借完教室。天數多,可能不符合;但天數少,也未必不行。

所以用二分,check的時候用前綴和表示某一天借出的教室數

比如前綴和最開始是0 0 0 0 0

第3天到第5天借出2個教室

數列就變成0 0 2 0 -2

最後計算數列中每天的借出教室的值是否超過準備的教室

代碼寫的比較醜,湊合著看看吧

 1 #include<stdio.h>
 2 #include<algorithm>
 3 #include<string.h>
 4 using namespace std;
 5 const int mn=1000777;
 6 int num[mn],R[mn],d[mn],s[mn],t[mn],m,n,ans;
 7 template<class T>void read(T &x)
 8 {
 9     x=0;char ch=getchar();
10     while(ch<0||ch>9)   ch=getchar();
11     while(ch>=0&&ch<=9){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
12     return;
13 }
14 bool check(int a)
15 {
16     int sum=0;
17     memset(num,0,sizeof(num));
18     for(int i=1;i<=a;i++)
19     {
20         num[s[i]]+=d[i];
21         num[t[i]+1]-=d[i];       
22     }
23     for(int i=1;i<=n;i++)
24     {
25         sum+=num[i];  
26         if(sum>R[i]) return 0;
27     }
28     return 1;
29 }
30 int main()
31 {
32     read(n),read(m);
33     for(int i=1;i<=n;++i)
34       read(R[i]);
35     for(int i=1;i<=m;++i)
36       read(d[i]),read(s[i]),read(t[i]);      
37     int l=1,r=m;
38     while(l<=r)
39     {
40         int mid=(l+r)>>1;
41         if(check(mid)) l=mid+1;
42         else           {ans=mid;r=mid-1;} 
43     }
44     if(ans==0) printf("0");
45     else       printf("-1\n%d",ans);
46     return 0;
47 }

NOIP2012借教室