题目链接:(https://www.luogu.com.cn/problem/P2345)
这道题大多数人的解法是根据v来进行排序,而我则是用的排序x的方法,看见还没人发就来发一篇.(这道题数据真滴水啊)
解题步骤
首先我们可以看到,题目中给的是许多奶牛的坐标和听力值,我们很容易联想到把其中中一个元素进行排序,这样子就可以只用考虑其中的一个元素了 然而我解这道题的时候联系到了三值偏序问题,(将a作为第一关键字排序后再用归并排须排序b值的时候无论怎么变换,在范围[l,mid]里面的a值都会小于范围[mid+1,r]中的a值,然后就是骚操作来了),所以我就排序了奶牛的坐标
接着我又利用归并排须,排序v的同时计算出答案
具体解题细节
首先:一定要记得开longlong
答案的计算公式
解释一下:大家应该都知道归并排序的原理,就是两个序列进行合并。而在此算法中第一个序列的“x”数值一定小于任意一个第二个序列的“x”数值
rt记录的就是目前已经被合并的第二序列的数的“x”数值之和
lt记录的就是目前已经被合并的第一序列的数的“x”数值之和
t1表示的就是目前到了序列一的第几个位置了,t1-l就是当前节点减去起点就是已经合并的序列1的数的个数
然后t2表示的就是目前到了序列2的第几个位置了,t2-mid-1就是当前节点减去起点就是已经合并的序列2的数的个数
1.res+=T[t1].v*(rt-(t2-mid-1)*T[t1].x),t1++;//如果当前节点是归并排序中属于序列1合并来的,那么它的x一定比已经合并的属于序列2的数的x值小, // 对于任意的序列1中的“x”数值一定小于序列二中的数值,所以任意序列2中的“x”数值减去序列1中的 “x”数值一定大于0, // 同时又因为序列2中的每一个数都要减去当前这个合并进去的数的“x”数值, // 所以直接用目前已经被合并的第二序列的数的“x”数值之和减去已经合并的序列2的数的个数与当前点的“x”数值的乘积就行了 2.res+=T[t2].v*((t1-l)*T[t2].x-lt),t2++;//如果是当前节点在归并排序中属于序列2合并来的,那么它的x一定比前面的大,上面解释的很详细,不赘述了。
(还有就是我在此处通过一点点的推断可以去除绝对值,具体见答案计算以及上面提到的“在范围[l,mid]里面的a值都会小于范围[mid+1,r]中的a值”)
CODE
#include <bits/stdc++.h> using namespace std; #define int long long struct node{int v,x;}T[20005],b[20005]; int n,res=0; int cmp(node A,node B){ if(A.x != B.x)return A.x<B.x; return A.v<B.v; } int CDQ(int l , int r){ if(l == r)return 0; int mid=(l+r)>>1; CDQ(l,mid);CDQ(mid+1,r); int t1=l,t2=mid+1,lt=0,rt=0; for (int i = l ; i <= r ; i ++){ if(T[t1].v <= T[t2].v && t1 <= mid || t2 > r ) b[i]=T[t1],lt+=T[t1].x,//合并序列的同时计算答案 res+=T[t1].v*(rt-(t2-mid-1)*T[t1].x),t1++; else b[i]=T[t2],rt+=T[t2].x, res+=T[t2].v*((t1-l)*T[t2].x-lt),t2++;; } for (int i = l ; i <= r ; i ++)T[i]=b[i]; return 0; } signed main(){ cin>>n; for (int i = 1 ; i <= n ; i ++) cin>>T[i].v>>T[i].x; sort(T+1,T+1+n,cmp); CDQ(1,n); cout<<res; return 0; }