UValive 4329 --樹狀陣列入門
阿新 • • 發佈:2018-12-17
白書上樹狀陣列的例題,剛好學完,正好熟悉一下樹狀陣列的常規操作。
對於每個i當裁判時的情況,設從a1到ai-1有ci個小於ai的數,則就有i-1-ci個比ai大的數,從ai+1到an有di個小於ci的數,就有n-i-di個比ai大的數,這樣,大數與小數配對,小數與大數配對,就有ci*(n-i-di)+di*(i-1-ci)種不同的組法,問題就變成求ci和di。
如何求ci和di呢?從0到ai的最大值1e5遍歷一遍,如果這個值存在(判斷在a[i]中是否出現),x[a[i]]=1,否則等於0,這樣,小於a[i]的值的個數就變為x[1]+x[2]+......+x[a[i]-1](仔細想想),那麼問題就變成如何求x[i]得字首和了,就可以用樹狀陣列求了。具體實現就是,輸入一個a[i],將它用add操作加入到樹狀陣列中,用sum求字首和。還有很多細節,看程式碼吧。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #include <queue> using namespace std; typedef long long ll; const int maxn=2e4+10; const int maxm=1e5+10; int n; int c[maxn]; int d[maxn];//標號小於和大於i的,技能值比ai小的個數 int a[maxn]; int s[maxm];//樹狀陣列 int lowbit(int x) { return x&-x; } int sum(int x) { int ret=0; while(x>0) { ret+=s[x]; x-=lowbit(x);//向左遞推,求和 } return ret; } void add(int x,int d) { while(x<maxm)//這裡注意一下,你求的是值的字首和 { s[x]+=d; x+=lowbit(x);//向右遞推,修改元素 } } int main() { int T; scanf("%d",&T); while(T--) { memset(s,0,sizeof(s)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } //利用樹狀陣列求c[i] for(int i=1;i<=n;i++) { add(a[i],1); c[i]=sum(a[i])-1;//直接求c[i]的字首和,自己好好想象一下樹狀陣列是如何儲存字首和的 } memset(s,0,sizeof(s));//記得初始化 for(int i=n;i>=1;i--) { add(a[i],1); d[i]=sum(a[i])-1;//字尾和,滿足d[i]陣列的定義 } ll ans=0; for(int i=2;i<n;i++) { ans+=c[i]*(n-i-d[i])+d[i]*(i-c[i]-1); } printf("%lld\n",ans); } return 0; }