【Codeforces 956D】Contact ATC 题解

题面:传送门


    这场比赛我没现场打,只是闲得没事就Virtual Participate了一发。我怎么又开始随便找题做了?(雾

    一看前3题都是普及组难度,我激动极了,哗啦哗啦全写完了(但是第3题还wa了一次,调了半天,可能我还是太弱了)。

    看到了 D 题,也就是这一题,总觉得不怎么难,但就是没有思路。在快结束时我终于想出了方法并且交了一发wa。结束后才发现我把分数判断大小时分母为负符号取反给忘了QAQ,蒟蒻不想活了。。。

    但是怎么能因为一道题没过就不活了呢?我们还是得保持乐观的。好了,言归正传,我来讲讲我的思路。

    我们会发现,只有当两架飞机的速度与离原点的距离形成的比例相同时,它们才会在原点相遇,可是不幸的是,速度不是一定的,甚至有无穷大种可能。

    那怎么做呢?由于风速区间为 [ w , w ] ,我们从一个端点 w 做起,也就是让所有飞机的速度减 w ,算出每架飞机的比例等于 v w x ,按这个值从小到大排个序,这时开始同时给速度增加相同的量,我们会发现,两架飞机的算出来的值的大小变化是单调的,要不就差得越来越远,要不就越来越近甚至小的超过大的,那么当我们把速度变为 v + w 时,只要原来第一架比另第二架小,最后第一架比第二架大, 那么中途一定会遇到相等的情况,也就是说会在原点相遇。

    总的来说,就是把飞机先按 v w x 排个序,记录下顺序,再按 v + w x 排个序,记录下位置。在第一种顺序下从头到尾跑一遍,用树状数组修改查询得到每架飞机与之前多少架飞机在原点相遇,最后加起来就是答案。

    注意一开始的值相等要放在一起算,排序时分母为负大小关系要取反!!!

    复杂度为 Θ ( n l o g 2 n ) 。全部代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,w;
int x[100010],v[100010];
int s1[100010],s2[100010];
int p[100010];
bool cmp1(int a,int b)
{
    if(1LL*x[a]*x[b]>0)return (-1LL*(v[a]-w)*x[b])<(-1LL*(v[b]-w)*x[a]);
    return (-1LL*(v[a]-w)*x[b])>(-1LL*(v[b]-w)*x[a]);
}
bool cmp2(int a,int b)
{
    if(1LL*x[a]*x[b]>0)return (-1LL*(v[a]+w)*x[b])>(-1LL*(v[b]+w)*x[a]);
    return (-1LL*(v[a]+w)*x[b])<(-1LL*(v[b]+w)*x[a]);
}
bool eq1(int a,int b)
{
    return (-1LL*(v[a]-w)*x[b])==(-1LL*(v[b]-w)*x[a]);
}
bool eq2(int a,int b)
{
    return (-1LL*(v[a]+w)*x[b])==(-1LL*(v[b]+w)*x[a]);
}
int sum[100010];
void add(int x,int v)
{
    for(int i=x;i<=n;i+=i&-i)sum[i]+=v;
}
int query(int x)
{
    int res=0;
    for(int i=x;i;i-=i&-i)res+=sum[i];
    return res;
}
long long ans;
int main()
{
    scanf("%d%d",&n,&w);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",x+i,v+i);
        s1[i]=s2[i]=i;
    }
    sort(s1+1,s1+n+1,cmp1);
    sort(s2+1,s2+n+1,cmp2);
    int cur=1;
    for(int i=1;i<=n;i++)
    {
        if(i>1 && !eq2(s2[i-1],s2[i]))cur++;
        p[s2[i]]=cur;
    }
    for(int i=1;i<=n;i++)
    {
        int j=i;
        while(j<=n && eq1(s1[i],s1[j]))j++;
        j--;
        for(int k=i;k<=j;k++)ans+=query(p[s1[k]]);
        ans+=1LL*(j+1-i)*(j-i)/2;
        for(int k=i;k<=j;k++)add(p[s1[k]],1);
        i=j;
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42112677/article/details/80470869