BZOJ1805[Ioi2007]Sail船帆——線段樹+貪心
阿新 • • 發佈:2018-11-10
題目描述
讓我們來建造一艘新的海盜船。船上有 N個旗杆,每根旗杆被分成單位長度的小節。旗杆的長度等於它被分成的小節的數目。每根旗杆上會掛一些帆,每張帆正好佔據旗杆上的一個小節。在一根旗杆上的帆可以任意排布在不同的小節中,但是每個小節上至多能掛一張帆。 在風中,帆的不同排布方式會產生不同的推動力。靠近船頭的帆比它後面的相同高度上的帆獲得的推動力少,換句話說,靠近船頭的帆的推動力由於受它後面相同高度的帆的影響而打了折扣。對於任意一張帆,它的推動力折扣等於在它後面並且和它在同一高度的帆的數目。 所有帆的任意一種位置組合的推動力折扣總和等於在該位置下所有帆的推動力折扣的總和。 前 後 這條船上有6個旗杆,從前(圖上的左邊)到後的高度分別是3,5,4,2,4和3。圖中所示的帆的位置組合的總推動力折扣是10。上圖給出了每張帆自己的推動力折扣。 任務 給定N個旗杆的高度和每個旗杆上掛的帆的數目,寫一個程式求出所有位置組合中的可能達到的最小的總推動力折扣。輸入
輸出
輸出包含一個整數,表示可以達到的最小的總推動力折扣。 注意:計算和輸出結果時使用64位整數型別(在C/C++中用long long, 在Pascal中用int64)。 評分 在25分的測試資料中,帆的不同位置組合的總數目不超過1 000 000。樣例輸入
63 2
5 3
4 1
2 1
4 3
3 2
樣例輸出
10這個樣例資料和上頁中圖示的樣例相同。
#include<set> #include<map> #include<queue> #include<cmath> #include<stack> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; struct miku { int h; int s; }a[100010]; int mn[800010]; int mx[800010]; int sum[800010]; int n,m; ll ans; bool cmp(miku a,miku b) { return a.h<b.h; } void pushup(int rt) { mn[rt]=min(mn[rt<<1],mn[rt<<1|1]); mx[rt]=max(mx[rt<<1],mx[rt<<1|1]); } void pushdown(int rt) { if(sum[rt]) { sum[rt<<1]+=sum[rt]; sum[rt<<1|1]+=sum[rt]; mn[rt<<1]+=sum[rt]; mn[rt<<1|1]+=sum[rt]; mx[rt<<1]+=sum[rt]; mx[rt<<1|1]+=sum[rt]; sum[rt]=0; } } void change(int rt,int l,int r,int L,int R) { if(L<=l&&r<=R) { sum[rt]++; mx[rt]++; mn[rt]++; return ; } pushdown(rt); int mid=(l+r)>>1; if(L<=mid) { change(rt<<1,l,mid,L,R); } if(R>mid) { change(rt<<1|1,mid+1,r,L,R); } pushup(rt); } int query_mid(int rt,int l,int r,int k) { if(l==r) { return mn[rt]; } pushdown(rt); int mid=(l+r)>>1; if(k<=mid) { return query_mid(rt<<1,l,mid,k); } else { return query_mid(rt<<1|1,mid+1,r,k); } } int query_left(int rt,int l,int r,int k) { if(l==r) { return l; } pushdown(rt); int mid=(l+r)>>1; if(mn[rt<<1]<=k) { return query_left(rt<<1,l,mid,k); } else { return query_left(rt<<1|1,mid+1,r,k); } } int query_right(int rt,int l,int r,int k) { if(l==r) { return l; } pushdown(rt); int mid=(l+r)>>1; if(mx[rt<<1|1]>=k) { return query_right(rt<<1|1,mid+1,r,k); } else { return query_right(rt<<1,l,mid,k); } } void count_ans(int rt,int l,int r) { if(l==r) { ans+=1ll*mn[rt]*(mn[rt]-1)/2; return ; } pushdown(rt); int mid=(l+r)>>1; count_ans(rt<<1,l,mid); count_ans(rt<<1|1,mid+1,r); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&a[i].h,&a[i].s); m=max(m,a[i].h); } sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) { int res=query_mid(1,1,m,a[i].h-a[i].s+1); int L=query_left(1,1,m,res); int R=query_right(1,1,m,res); if(R<a[i].h) { int len=a[i].h-R; change(1,1,m,R+1,a[i].h); change(1,1,m,L,L+(a[i].s-len)-1); } else { change(1,1,m,L,L+a[i].s-1); } } count_ans(1,1,m); printf("%lld",ans); }