1. 程式人生 > >【BZOJ4293】Siano(PA2015)-線段樹+二分

【BZOJ4293】Siano(PA2015)-線段樹+二分

測試地址:Siano 題目大意:nn片草,第ii片草每天長高aia_i米,mm次操作,每次操作在第dd天把所有草高於某個數bb的部分割掉,求每次割下的草的高度和。 做法: 本題需要用到線段樹+二分。 挺神的一道題…注意到每次割草,被割的草都不一定是一個區間,很難處理,因此想到通過某種辦法把每次操作的修改變成一個區間。 觀察發現,在任何時刻,aia_i大的草的高度一定不會比aia_i小的草矮。因此我們把草按aia_i從小到大排序,這樣每次被割的草一定是一段字尾,只需要二分就可以找到這樣的字尾。 涉及區間修改,還要支援二分,所以想到線上段樹上二分。我們要維護的是每片草現在的高度,那麼第n

owdaynowday天第ii片草的高度是:lasthi+(nowdaylastdayi)ailasth_i+(nowday-lastday_i)\cdot a_i,其中lastdayilastday_i為這片草上次被割的時間,lasthilasth_i為這片草上次被割後剩下的高度。顯然這個可以寫成nowdayai+(lasthilastdayiai)nowday\cdot a_i+(lasth_i-lastday_i\cdot a_i),前面的部分僅隨著nowdaynowday的變化而變化,而後面的資訊都可以線上段樹上維護出來。在詢問時,答案就是被割的草的高度之和,再減去這些草的片數乘上bb。這樣我們就以O(nlogn)O(n\log n)的時間複雜度解決了此題。 以下是本人程式碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
ll a[500010]={0},sum[500010],d[500010],b[500010];
ll nowday,based[2000010]={0},rhtbased[
2000010]; int rhtop[2000010]={0},optag[2000010]={0}; void update(int no,int op,int l,int r) { rhtop[no]=optag[no]=op; based[no]=b[optag[no]]*(ll)(r-l+1)-d[optag[no]]*(sum[r]-sum[l-1]); rhtbased[no]=b[optag[no]]-d[optag[no]]*a[r]; } void pushdown(int no,int l,int r) { int mid=(l+r)>>1; if (optag[no]>0) { update(no<<1,optag[no],l,mid); update(no<<1|1,optag[no],mid+1,r); optag[no]=0; } } void pushup(int no) { rhtop[no]=rhtop[no<<1|1]; rhtbased[no]=rhtbased[no<<1|1]; based[no]=based[no<<1]+based[no<<1|1]; } int find(int no,int l,int r,ll x) { if (l==r) { if (nowday*a[l]+based[no]<=x) return l+1; else return l; } int mid=(l+r)>>1; pushdown(no,l,r); if (nowday*a[mid]+rhtbased[no<<1]>x) return find(no<<1,l,mid,x); else return find(no<<1|1,mid+1,r,x); } void modify(int no,int l,int r,int x,int op) { if (l>=x) { update(no,op,l,r); return; } int mid=(l+r)>>1; pushdown(no,l,r); if (mid>=x) modify(no<<1,l,mid,x,op); modify(no<<1|1,mid+1,r,x,op); pushup(no); } ll query(int no,int l,int r,int x) { if (l>=x) return nowday*(sum[r]-sum[l-1])+based[no]; ll ans=0; int mid=(l+r)>>1; pushdown(no,l,r); if (mid>=x) ans+=query(no<<1,l,mid,x); ans+=query(no<<1|1,mid+1,r,x); return ans; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1); sum[0]=0; for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]; for(int i=1;i<=m;i++) { scanf("%lld%lld",&d[i],&b[i]); nowday=d[i]; int pos=find(1,1,n,b[i]); if (pos==n+1) printf("0\n"); else { printf("%lld\n",query(1,1,n,pos)-(ll)(n-pos+1)*b[i]); modify(1,1,n,pos,i); } } return 0; }