奶牛集會
阿新 • • 發佈:2018-11-01
題目
https://www.luogu.org/problemnew/show/P2345#sub
思路
將陣列先按v值排序
然後找到中點mid,左右遞迴處理
因為v值排過序,所以右邊的v值一定大於左邊v值
就剩x不好算了
看到絕對值,最簡單的方法就是去絕對值符號
所以我們應該列舉右邊mid+1到r的區間,找到有哪些x值比當前小,哪些比它大
右邊的v值一定大於左邊v值,求和乘a[i].v就行了
但是,這又該怎麼做呢?
也許左右x值為升序就好做了,這就像求逆序對一樣
因此我們再對序列進行歸併排序
儘管這會對v值的升序進行破環,但由於中間的劃分,所以左右不會混合,前面那個 性質還能保證
程式碼
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int maxn=100000+10; struct nod { long long v,x; }s[maxn]; struct node { int left,right; long long sum1,sum2; }t[maxn*4]; long long n,maxs,ans; bool cmp(nod a,nod b) { return a.v<b.v; } void build(int g,int l,int r) { t[g].left=l;t[g].right=r;t[g].sum1=t[g].sum2=0; if(l==r) return; int mid=(l+r)>>1; build(g<<1,l,mid);build(g<<1|1,mid+1,r); } long long get(int g,int l,int r,int opt) { if(r<t[g].left || t[g].right<l) return 0; if(l<=t[g].left&&t[g].right<=r) { if(opt==1) return t[g].sum1; if(opt==2) return t[g].sum2; } return get(g<<1,l,r,opt)+get(g<<1|1,l,r,opt); } void add(int g,int x,long long y) { if(t[g].left==t[g].right) { t[g].sum1+=y;t[g].sum2++;return; } if(x<=t[g<<1].right) add(g<<1,x,y); else add(g<<1|1,x,y); t[g].sum1=t[g<<1].sum1+t[g<<1|1].sum1; t[g].sum2=t[g<<1].sum2+t[g<<1|1].sum2; } int main() { scanf("%lld",&n); for(int i=1;i<=n;i++) { scanf("%lld%lld",&s[i].v,&s[i].x); maxs=max(maxs,s[i].x); } sort(s+1,s+n+1,cmp); build(1,1,maxs); for(int i=1;i<=n;i++) { long long g=get(1,s[i].x+1,maxs,1);//距離 long long k=get(1,s[i].x+1,maxs,2);//個數 ans+=s[i].v*(g-k*s[i].x); g=get(1,1,s[i].x-1,1); k=get(1,1,s[i].x-1,2); ans+=s[i].v*(k*s[i].x-g); add(1,s[i].x,s[i].x); } printf("%lld\n",ans); }