1. 程式人生 > >SCOI2007 組隊

SCOI2007 組隊

來看 lims clu 機會 時間 pro 如果 就是 type

傳送門

這道題該怎麽做呢……我自己只能想出來O(n^3)的暴力模擬……不過並不行。

來看一下正解吧……將給定的式子變一下形,得到A*height + B*speed - C <= A*minh+B*mins.

這樣的話,把所有的運動員按照A*height + B*speed - C從小到大排序,這樣就使得,如果有i成立,那麽對於每個j<i,只要這個j的height和speed都不小於最小要求都是合法的。

但是不只有這一個限制。由於height還必須大於等於minh,所以對於任意一個運動員,其speed不僅要大於等於mins還要小於等於mins + C/B(這個用上面的式子可以推出)所以我們進行枚舉,先枚舉mins,之後再枚舉minh,對於當前的minh,我們按照sum的順序從小到大去枚舉,如果這個運動員滿足其speed >=mins && <= mins + C/B,那麽這個運動員當前就是可以取的。這樣我們維護了一個右區間,之後,我們再維護左區間,對於每一個height不夠的運動員,我們把他從合法區間中踢出。不過有一些本身並沒有被算過,所以只有對於s符合要求的那些,我們才會把其刪除。這樣每次在找完合法區間之後更新答案即可。

這種算法起到優化的作用在於其利用了單調性。我們來看,在從小到大枚舉minh,mins的過程中,我們知道A*minh+B*mins.必然是單調遞增的,也就是說,每次向後取一個元素,就會導致更多的人有被選中的機會。而對於已經被刪除的人,因為我們枚舉的最小高度肯定也是遞增的,所以已經被刪除的人將來也不可能合法。於是這個算法就用單調性來保證了它的正確性,同時完成了時間復雜度的優化。我們只需要O(2*n^2)的復雜度就可以過了。

看一下代碼。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include
<cmath> #define rep(i,a,n) for(ll i = a;i <= n;i++) #define per(i,n,a) for(ll i = n;i >= a;i--) #define enter putchar(‘\n‘) using namespace std; const int M = 5005; typedef long long ll; int read() { int 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 node { int h,s,sum; }Sum[M],H[M],S[M]; bool cmp1(node a,node b) { return a.sum < b.sum; } bool cmp2(node a,node b) { return a.h < b.h; } bool cmp3(node a,node b) { return a.s < b.s; } int n,A,B,C,l,r,cnt,mins,lims,limsum,ans; int main() { n = read(),A = read(),B = read(),C = read(); rep(i,1,n) { Sum[i].h = read(),Sum[i].s = read(),Sum[i].sum = Sum[i].h * A + Sum[i].s * B - C; H[i].h = Sum[i].h,H[i].s = Sum[i].s,H[i].sum = Sum[i].sum; S[i].h = Sum[i].h,S[i].s = Sum[i].s,S[i].sum = Sum[i].sum; } sort(Sum+1,Sum+1+n,cmp1); sort(H+1,H+1+n,cmp2); sort(S+1,S+1+n,cmp3);//上面是按照關鍵字排序 rep(i,1,n) { l = 0,r = 0,cnt = 0; mins = S[i].s,lims = S[i].s + C / B;//確定限制範圍 rep(j,1,n) { limsum = A * H[j].h + B * mins; while(r < n && Sum[r+1].sum < limsum) { r++; if(mins <= Sum[r].s && Sum[r].s <= lims) cnt++;//將合法元素選中 }//判斷合法區間的末尾 while(l < n && H[l+1].h < H[j].h) { l++; if(mins <= H[l].s && H[l].s <= lims) cnt--; }//判斷合法區間開頭並且刪除不合法元素 ans = max(ans,cnt);//更新答案 } } printf("%d\n",ans); return 0; }

SCOI2007 組隊