题解【CF1311F Moving Points】

\[ \texttt{Preface} \]

赛时,把 " 任意时刻 " 理解成 " 整数时刻 " 了,看起来一脸不可做的亚子,还各种推式子。

话说我为什么觉得 E 比 F 还难。
\[ \texttt{Description} \]
一个坐标轴 \(OX\) 上有 \(n\) 个点,第 \(i\) 个点位于整数点 \(x_i\) ,速度为 \(v_i\)

所有点以恒定的速度移动,在时刻 \(t\)\(t\) 可以是非整数),第 \(i\) 个点的坐标可以被计算为 \(x_i + t \times v_i\)

定义 \(d(i,j)\) 为:任何可能的时刻,第 \(i\) 个点和第 \(j\) 个点的最小可能距离,即
\[ \min\limits_{t\in[0,∞)} \{ |(x_i+t \times v_i)-(x_j+t \times v_j)| \} \]
\(\sum\limits_{1 \leq i < j \leq n}d(i,j)\)
\[ \texttt{Solution} \]

  • 考虑任意两个点 \(i,j\) ,不妨设 \(x_i \leq x_j\) ,位置关系如下图:

  1. \(v_i \leq v_j\) 时:

    说明点 \(i\) 的速度不比点 \(j\) 快,意味着每过一秒,点 \(i,j\) 的距离会增大 \(v_j-v_i\) ,由于 \(t\times(v_j-v_i) \geq 0\) ,所以点 \(i,j\) 的距离永远不降,故时刻 \(0\) 时,\(d(i,j)\) 最小,此时 \(d(i,j)=x_j-x_i\)

  2. \(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\) 的贡献,也就是 \(\sum\limits_{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 \times c_1-c_2\) ,最后再将点 \(i\) 插入 BIT ,扫描完即可求出答案。
  • \(\mathcal{O(n \log n)}\)

\[ \texttt{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\n",ans);

    return 0;
}

\[ \texttt{Thanks} \ \texttt{for} \ \texttt{watching} \]

猜你喜欢

转载自www.cnblogs.com/cjtcalc/p/12361012.html