这道题我感觉评成紫题并不夸张,这比某些紫题思维含量高多了……反正我好久才想明白怎么做……
其实,这道题就是求这个式子所有的 “ max{ Vi ; Vj } × | Xi − Xj | ” 之和。由于n的范围20000,显然 n2是过不了的。(但是洛谷数据问题,n2能过,我也是醉了……)
那我们分析一下:对于一对 i , j ,能做出贡献的只有大的 v 所以我们对于一个 v ,一定是找比 v 小的去累计贡献,所以我们要从小到大枚举 v 。
那我们考虑对于这样的一个 v 的贡献:
20000 ————B段———— v —————A段——— 1
一下考虑都是小于 v 的其他 v ' 。
对于A段和B段,我们发现想求贡献就需要知道A 、B段的距离和,和点的个数。这两个用线段树维护即可,那么我们的问题就在如何锁定 v ' 。
其实这个很简答,在从小打到枚举 v 的同时处理线段树,这样我们每一次考虑的就是小于 v 的 v ' 了。
这道题真的挺好,值得一做。
北京市第三区交委提醒您:做题不long long,亲人两行泪。
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<vector> using namespace std; #define maxn 80000 #define int long long #define ls now<<1 #define rs now<<1|1 struct node { int v,x; } q[maxn]; int l[maxn],r[maxn],x[maxn],num[maxn]; int n,ans; void up(int now) { num[now]=num[ls]+num[rs]; x[now]=x[ls]+x[rs]; } bool cmp(node a,node b) { return a.v<b.v; } void build(int now,int L,int R) { l[now]=L; r[now]=R; if(L==R) return ; int mid=(L+R)>>1; build(ls,L,mid); build(rs,mid+1,R); } void update(int now,int id) { if(l[now]==r[now]&&l[now]==id) { num[now]++; x[now]=id; return ; } int mid=(l[now]+r[now])>>1; if(id<=mid) update(ls,id); else update(rs,id); up(now); } int query_num(int now,int L,int R) { if(l[now]==L&&r[now]==R) return num[now]; int mid=(l[now]+r[now])>>1; if(R<=mid) return query_num(ls,L,R); else if(L>mid) return query_num(rs,L,R); else return query_num(ls,L,mid)+query_num(rs,mid+1,R); } int query_sum(int now,int L,int R) { if(l[now]==L&&r[now]==R) return x[now]; int mid=(l[now]+r[now])>>1; if(R<=mid) return query_sum(ls,L,R); else if(L>mid) return query_sum(rs,L,R); else return query_sum(ls,L,mid)+query_sum(rs,mid+1,R); } void solve() { for(int i=1;i<=n;i++) { int x=q[i].x; int v=q[i].v; ans+=v*(query_num(1,1,x)*x-query_sum(1,1,x)) +v*(query_sum(1,x,20000)-query_num(1,x,20000)*x); update(1,x); } } main() { scanf("%lld",&n); for(int i=1; i<=n; i++) scanf("%lld%lld",&q[i].v,&q[i].x); sort(q+1,q+n+1,cmp); build(1,1,20000); solve(); printf("%lld ",ans); return 0; }