题解CF1311F Moving Points
Posted cjtcalc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解CF1311F Moving Points相关的知识,希望对你有一定的参考价值。
[ exttt{Preface} ]
赛时,把 " 任意时刻 " 理解成 " 整数时刻 " 了,看起来一脸不可做的亚子,还各种推式子。
话说我为什么觉得 E 比 F 还难。
[
exttt{Description}
]
一个坐标轴 (OX) 上有 (n) 个点,第 (i) 个点位于整数点 (x_i) ,速度为 (v_i) 。
所有点以恒定的速度移动,在时刻 (t) ( (t) 可以是非整数),第 (i) 个点的坐标可以被计算为 (x_i + t imes v_i) 。
定义 (d(i,j)) 为:任何可能的时刻,第 (i) 个点和第 (j) 个点的最小可能距离,即
[
minlimits_{tin[0,∞)} { |(x_i+t imes v_i)-(x_j+t imes v_j)| }
]
求 (sumlimits_{1 leq i < j leq n}d(i,j)) 。
[
exttt{Solution}
]
- 考虑任意两个点 (i,j) ,不妨设 (x_i leq x_j) ,位置关系如下图:
当 (v_i leq v_j) 时:
说明点 (i) 的速度不比点 (j) 快,意味着每过一秒,点 (i,j) 的距离会增大 (v_j-v_i) ,由于 (t imes(v_j-v_i) geq 0) ,所以点 (i,j) 的距离永远不降,故时刻 (0) 时,(d(i,j)) 最小,此时 (d(i,j)=x_j-x_i) 。
当 (v_i>v_j) 时:
说明点 (i) 的速度比点 (j) 快,意味着每过一秒,点 (i,j) 的距离会减少 (v_i-v_j) ,突然想到
小学数学的追及问题 ,显然可得,在时刻 (frac{x_j-x_i}{v_i-v_j}) 时,点 (i) 与点 (j) 重合,故 (d(i,j)=0) 。
我们发现情况 (2) 对答案没有贡献,实际上只是求情况 (1) 的贡献,也就是 (sumlimits_{x_i leq x_j & v_i leq v_j}(x_j-x_i)) 。
然后我们发现这是一个经典的偏序问题,具体的:
- 我们将所有点按 (x) 这一维从小到大排序,就可以直接去掉 (x_i leq x_j) 的偏序关系了。
- 将 (v) 这一维离散化,建立两个 BIT(当然线段树也行),一个用于维护当前扫描到的点中, (v) 值在区间内的点的个数,一个用于维护 (v) 值在区间内的点的 (x) 值和。
- 接下来我们扫描每个点 (i) ,计算将 (i) 当成位置靠后的那个点时所产生的贡献,当固定住 (i) 时,此时 (x_i) 是不变的,我们记 (v) 值小于等于 (v_i) 的点的数量为 (c_1) , (v) 值小于等于 (v_i) 点的 (x) 值和为 (c_2) ,贡献为 (x_i imes c_1-c_2) ,最后再将点 (i) 插入 BIT ,扫描完即可求出答案。
(mathcal{O(n log n)}) 。
[ exttt{Code} ]
#include<cstdio>
#include<algorithm>
#define RI register int
using namespace std;
namespace IO
{
static char buf[1<<20],*fs,*ft;
inline char gc()
{
if(fs==ft)
{
ft=(fs=buf)+fread(buf,1,1<<20,stdin);
if(fs==ft)return EOF;
}
return *fs++;
}
#define gc() getchar()
inline int read()
{
int x=0,f=1;char s=gc();
while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
return x*f;
}
}using IO::read;
const int N=200100;
int n;
struct Node{
int x;
int v;
}a[N];
bool cmp(Node a,Node b)
{
return a.x<b.x;
}
int len,mapval[N];
void discrete() // 离散化
{
sort(mapval+1,mapval+1+len);
len=unique(mapval+1,mapval+1+len)-mapval-1;
}
int Real(int x)
{
return lower_bound(mapval+1,mapval+1+len,x)-mapval;
}
long long c[N][2]; // 0 维是数量 , 1 维是 x 值和
void add(int x,int k,int val)
{
for(;x<=len;x+=x&-x)c[x][k]+=val;
}
long long ask(int x,int k)
{
long long ans=0;
for(;x;x-=x&-x)ans+=c[x][k];
return ans;
}
long long ans;
int main()
{
n=read();
for(RI i=1;i<=n;i++)
a[i].x=read();
for(RI i=1;i<=n;i++)
mapval[++len]=a[i].v=read();
sort(a+1,a+1+n,cmp); // 排序
discrete();
for(RI i=1;i<=n;i++)
a[i].v=Real(a[i].v);
for(RI i=1;i<=n;i++)
{
ans+=a[i].x*ask(a[i].v,0)-ask(a[i].v,1); // 计算
add(a[i].v,0,1),add(a[i].v,1,a[i].x); // 查询
}
printf("%lld
",ans);
return 0;
}
[ exttt{Thanks} exttt{for} exttt{watching} ]
以上是关于题解CF1311F Moving Points的主要内容,如果未能解决你的问题,请参考以下文章