HihoCoder 1488 : 排隊接水(莫隊+樹狀數組)
阿新 • • 發佈:2018-02-20
pri coder esp 適合 兩個 long long 輸入 string sca
描述
有n個小朋友需要接水,其中第i個小朋友接水需要ai分鐘。
由於水龍頭有限,小Hi需要知道如果為第l個到第r個小朋友分配一個水龍頭,如何安排他們的接水順序才能使得他們等待加接水的時間總和最小。
小Hi總共會有m次詢問,你能幫助他解決這個問題嗎?
假設3個小朋友接水的時間分別是2,3,4。如果他們依次接水,第一位小朋友等待加接水的時間是2,第二位小朋友是5,第三位小朋友是9。時間總和是16。
輸入
第一行一個數T(T<=10),表示數據組數
對於每一組數據:
第一行兩個數n,m(1<=n,m<=20,000)
第二行n個數a1...an,表示每個小朋友接水所需時間(ai<=20,000)
接下來m行,每行兩個數l和r
輸出
對於每次詢問,輸出一行一個整數,表示答案。
樣例輸入
1 4 2 1 2 3 4 1 2 2 4
樣例輸出
4 16
思路:貪心可知,時間小的在前。但是排序是不可能的,需要更高效的方法,註意到ai<=2e5,適合用樹狀數組記錄a[i]的個數前綴和,以及a[i]的前綴和。
可以離線,所以用莫隊+樹狀數組,莫隊的話,第一次寫這中數學類型的轉移,開始還有點抵觸,但是拿出筆一劃,公式也不難。
對於暴力的公式,即L<=i<=R的所有i的前綴和:
for(i=L;i<=R;i++) for(j=L;j<=i;j++) sum+=a[j];
那麽現在加一個第i=x進去,則對新的 i=x,需要多累加前綴:
for(j=L;j<=x;j++) sum+=a[j];
對於後面的i>=x,都需要累加一個j=x,即a[x]*後面的個數。
for(i=x;i<=R;i++) for(j=L;j<=i;j++) sum+=a[j];
然後把上面的轉化為樹狀數組的前綴和即可。 兩個樹狀數組,分別記錄個數和累加和。
#include<cmath> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=20000; ll a[maxn+10],num[maxn+10],cnt,B,l,r,tmp; ll sum[maxn+10]; struct in{ ll L;ll R;ll id; ll ans;}s[maxn+10]; bool cmp(in x,in y){ if(x.L/B==y.L/B) return x.R/B<y.R/B; return x.L/B<y.L/B; } bool cmp2(in x,in y){ return x.id<y.id;} void update(){ cnt=0; memset(num,0,sizeof(num));memset(sum,0,sizeof(sum)); } void addnum(ll x,ll y) { while(x<=maxn){ num[x]+=y; x+=(-x)&x; } } void addsum(ll x,ll y) { while(x<=maxn){ sum[x]+=y; x+=(-x)&x; } } int querynum(int x){ ll res=0; while(x>0){ res+=num[x]; x-=(-x)&x;} return res; } int querysum(int x){ ll res=0; while(x>0){ res+=sum[x]; x-=(-x)&x;} return res;} int main() { ll T,n,m,i;ll tsum; scanf("%d",&T); while(T--){ update(); scanf("%lld%lld",&n,&m); for(i=1;i<=n;i++) scanf("%lld",&a[i]); for(i=1;i<=m;i++) s[i].id=i,scanf("%lld%lld",&s[i].L,&s[i].R); B=sqrt(n); sort(s+1,s+m+1,cmp); l=r=1; tmp=a[1]; addnum(a[1],1); addsum(a[1],a[1]); for(i=1;i<=m;i++){ while(l<s[i].L){ tsum=querysum(a[l]); tmp-=tsum; tmp-=(querynum(maxn)-querynum(a[l]))*a[l]; addnum(a[l],-1); addsum(a[l],-a[l]); l++; } while(l>s[i].L){ l--; addnum(a[l],1); addsum(a[l],a[l]); tsum=querysum(a[l]); tmp+=tsum; tmp+=(querynum(maxn)-querynum(a[l]))*a[l]; } while(r<s[i].R){ r++; addnum(a[r],1); addsum(a[r],a[r]); tsum=querysum(a[r]); tmp+=tsum; tmp+=(querynum(maxn)-querynum(a[r]))*a[r]; } while(r>s[i].R){ tsum=querysum(a[r]); tmp-=tsum; tmp-=(querynum(maxn)-querynum(a[r]))*a[r]; addnum(a[r],-1); addsum(a[r],-a[r]); r--; } s[i].ans=tmp; } sort(s+1,s+m+1,cmp2); for(i=1;i<=m;i++) printf("%lld\n",s[i].ans); } return 0; }
HihoCoder 1488 : 排隊接水(莫隊+樹狀數組)