题目描述 Description
一群青年人排成一队,用手机互相聊天。
每个人的手机有一个信号接收指标,第i个人的接收指标设为v[i]。
如果位置在x[i]的人要和位置在xj的人聊天,那么这两人组成的一对的信号发射强度就是abs(x[i]-x[j])*max(v[i],v[j]).
现在我们想知道,这些人所有对子中的信号发射强度的总和。
输入输出格式 Input/output
输入格式:
第一行一个整数N,接下来N行,每行两个整数v[i]和x[i]。
输出格式:
所有对的信号发射强度总和。
输入输出样例 Sample input/output
4
3 1
2 5
2 6
4 3
输出样例:
57
说明 description
对于40%的数据,N<=5,000
对于100%的数据,N<=100,000 1≤x[i]≤20,000
注意:可能有两人在同一个位置
答案在int64或long long范围内
题解
这题是luogu官方考试的一套题目,当时并没有深入思考,交了一份暴力拿了40,最后只拿到了40……………………。
这题是个不错的题,需要仔细想一下。
首先,暴力的解法就是读一个数处理一下,for(j=1->i-1),把ans加上那个值,最后输出ans即可。
但是暴力的复杂度是n^2,n == 10W,显然是不行的,于是我们就要优化一下:
首先是取max操作,每次取max很麻烦,我们不妨直接按v进行从小到大的排序,这样每次都是*v[i]了,不用取max了。
再对abs那个东西进行一些小的变形:
对于第i项,它的x左边的abs就是它本身,用暴力的方法写出来的话就是(x[i]-x[1]+x[i]-x[2]+x[i]-x[3]+……+x[i]-x[j])(这里要特别注意,这里减去的都是x坐标小于x[i]的,并且由于不会再csdn上写数学公式……于是就直接这么写吧,如果想要更详细证明的可以留言~我会付图),再将上面的和式优化一下:x[i]*tot(tot是小于x[i]的坐标个数和)-sigma(x[j]),这样就优化了很多了,下面就是如何快速求得tot了,对于数据范围支持log级别的操作,于是我们很容易想到线段树维护或者树状数组来维护,从代码复杂度来考虑还是打树状数组更优,于是我们可以用一个树状数组来维护前x位总共有几个人,每次查询是log级别的。对于当前人右边部分同样可以用上述的和式优化一下,用log级的时间求出来。听了这么多你一定糊涂了,所以我们来看一下代码吧~
函数部分
void cadd(int a,int x){ for(int i = a;i <= 23333;i += lowbit(i)) c[i] += x;}
int cquery(int a)
{
int ans = 0;
for(int i = a;i > 0;i -= lowbit(i)) ans += c[i];
return ans;
}
主函数部分
int lnum = cquery(poi[i].x-1);//查询到当前点x坐标为止前面有几个人
int rnum = i-cquery(poi[i].x);//查询到当前点坐标为止后面有几个人,因为当前已经加入了i个人,当前坐标(包括它自己)前面有cquery(poi[i].x)个人,相减即得到我们想要的答案。
这里有没有注意我写的是c add,是不是还有b add呢?b add的作用是什么呢?
大家应该注意到我只是快速求得了tot并没有快速求得sigma(x[j]),快速求得sigma(x[j])也是用树状数组:
cadd是直接+1,那么当前坐标的总和就是(当前坐标人数*当前坐标)了,这个我们可以用树状数组来搞,即(badd(x[i],x[i])),用代码实现就是下面这样:
void badd(int a,int x){ for(int i = a;i <= 23333;i += lowbit(i)) b[i] += x;}
int bquery(int a)
{
int ans = 0;
for(int i = a;i > 0;i -= lowbit(i)) ans += b[i];
return ans;
}
然后就是快速求得sigma(x[j]),这里我们的llsum就是左边的坐标和,rsum就是右边的坐标和。
int lsum = bquery(poi[i].x-1);
int rsum = bquery(23333)-bquery(poi[i].x);
最后我们直接相加相减,复杂度O(1)~
sum += (ll)(lnum*poi[i].x-lsum-rnum*poi[i].x+rsum)*(ll)(poi[i].v);
这样这个题就搞定了,我感觉这题思维难度还是不小的,考试的时候苦思冥想不得解,多打思维题提升脑洞能力~
下面附上完整代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
const int size = 200010;
void RD(int &x)
{
x = 0; char c; c = getchar();
int flag = 1;
if(c == '-') flag = -1;
while(c > '9' || c < '0') c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0',c = getchar();
x *= flag;
}
struct Poi{
int x,v;
bool operator < (const Poi &rhs) const
{
return v < rhs.v;
}
}poi[size];
int b[size],c[size];
void badd(int a,int x){ for(int i = a;i <= 23333;i += lowbit(i)) b[i] += x;}
int bquery(int a)
{
int ans = 0;
for(int i = a;i > 0;i -= lowbit(i)) ans += b[i];
return ans;
}
void cadd(int a,int x){ for(int i = a;i <= 23333;i += lowbit(i)) c[i] += x;}
int cquery(int a)
{
int ans = 0;
for(int i = a;i > 0;i -= lowbit(i)) ans += c[i];
return ans;
}
ll sum;
int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i ++) scanf("%d%d",&poi[i].v,&poi[i].x);
sort(poi+1,poi+1+n);
for(int i = 1;i <= n;i ++)
{
badd(poi[i].x,poi[i].x);
cadd(poi[i].x,1);
int lnum = cquery(poi[i].x-1);
int rnum = i-cquery(poi[i].x);
int lsum = bquery(poi[i].x-1);
int rsum = bquery(23333)-bquery(poi[i].x);
sum += (ll)(lnum*poi[i].x-lsum-rnum*poi[i].x+rsum)*(ll)(poi[i].v);
}
printf("%lld\n",sum);
return 0;
}
蒟蒻Orz各位大神