[Codeforces 956D]Contact ATC

本文针对一个特定的算法问题进行了深入探讨,通过数学转换简化了问题,并利用数据结构进行高效求解。采用一次函数特性,结合扫描线思想和树状数组维护,解决了两个飞机在不同风速条件下可能相遇的问题。

956D

题解:
考虑两个飞机 (x0,v0) ( x 0 , v 0 ) (x1,v1) ( x 1 , v 1 )
对于一个风速 w0 w 0 ,第一个飞机的飞行时间是 f0(w0)=x0v0+w0 f 0 ( w 0 ) = x 0 v 0 + w 0
第二个飞机的飞行时间是 f1(w0)=x1v1+w0 f 1 ( w 0 ) = x 1 v 1 + w 0
如果这两个飞机对答案有贡献,则说明这两个函数在 w0[w,w] w 0 ∈ [ − w , w ] 有交
考虑 g0=1f0 g 0 = 1 f 0 g1=1f1 g 1 = 1 f 1
显然这是两个一次函数,且若 g0 g 0 g1 g 1 有交则 f0 f 0 f1 f 1 有交.
对于一次函数,我们只要求出 g0 g 0 g1 g 1 [w,w] [ − w , w ] 的值域并且判断值域是否是一个区间真包含另一个区间即可。
然后类似扫描线的,用树状数组维护一下就行了。

#include<bits/stdc++.h>
#define LL long long
#define ull unsigned long long
#define ULL ull
#define mp make_pair
#define pii pair<int,int>
#define piii pair<int, pii >
#define pll pair <ll,ll>
#define pb push_back
#define big 20160116
#define INF 2147483647
#define pq priority_queue
#define rank rk124232
#define y1 y20160116
#define y0 y20160110
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
namespace Mymath{
    LL qp(LL x,LL p,LL mod){
        LL ans=1;
        while (p){
            if (p&1) ans=ans*x%mod;
            x=x*x%mod;
            p>>=1;
        }
        return ans;
    }
    LL inv(LL x,LL mod){
        return qp(x,mod-2,mod);
    }
    LL C(LL N,LL K,LL fact[],LL mod){
        return fact[N]*inv(fact[K],mod)%mod*inv(fact[N-K],mod)%mod;
    }
    template <typename Tp> Tp gcd(Tp A,Tp B){
        if (B==0) return A;
        return gcd(B,A%B);
    }
    template <typename Tp> Tp lcm(Tp A,Tp B){
        return A*B/gcd(A,B);
    }
};
namespace fwt{
    using namespace Mymath;
    void FWT(int a[],int n,LL mod)
    {
        for(int d=1;d<n;d<<=1)
            for(int m=d<<1,i=0;i<n;i+=m)
                for(int j=0;j<d;j++)
                {
                    int x=a[i+j],y=a[i+j+d];
                    a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
                    //xor:a[i+j]=x+y,a[i+j+d]=x-y;
                    //and:a[i+j]=x+y;
                    //or:a[i+j+d]=x+y;
                }
    }

    void UFWT(int a[],int n,LL mod)
    {
        LL rev=inv(2,mod);
        for(int d=1;d<n;d<<=1)
            for(int m=d<<1,i=0;i<n;i+=m)
                for(int j=0;j<d;j++)
                {
                    int x=a[i+j],y=a[i+j+d];
                    a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
                    //xor:a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;
                    //and:a[i+j]=x-y;
                    //or:a[i+j+d]=y-x;
                }
    }
    void solve(int a[],int b[],int n,LL mod)
    {
        FWT(a,n,mod);
        FWT(b,n,mod);
        for(int i=0;i<n;i++) a[i]=1LL*a[i]*b[i]%mod;
        UFWT(a,n,mod);
    }
};
const int Maxn=1e5+5;
LL x[Maxn],v[Maxn];
LL w;
pair<LL,LL> st[Maxn],ed[Maxn];
pair<LL,LL> dd[Maxn];
int ord[Maxn];
int id[Maxn];
bool cmp(int x,int y){
    if (st[x].first*st[y].second!=st[x].second*st[y].first){
        return st[x].first*st[y].second<st[x].second*st[y].first;
    }
    return ed[x].first*ed[y].second>ed[x].second*ed[y].first;
}
bool cmp2(pair<LL,LL> x,pair<LL,LL> y){
    return x.first*y.second<x.second*y.first;
}
int bit[Maxn];
void add(int pos){
    while (pos<Maxn){
        bit[pos]++;
        pos+=pos&-pos;
    }
}
int query(int pos){
    int ret=0;
    while (pos){
        ret+=bit[pos];
        pos-=pos&-pos;
    }
    return ret;
}
int main(){
    int n=read();
    w=read();
    for (int i=1;i<=n;i++){
        x[i]=read();v[i]=read();
        x[i]=-x[i];
        st[i]=mp(x[i],v[i]+w);
        ed[i]=mp(x[i],v[i]-w);
        /*
        if ((double)st[i].first/st[i].second>(double)ed[i].first/ed[i].second){
            swap(st[i],ed[i]);
        }*/
        if (st[i].first<0) st[i].first=-st[i].first,st[i].second=-st[i].second;
        if (ed[i].first<0) ed[i].first=-ed[i].first,ed[i].second=-ed[i].second;
        //cout<<"st"<<' '<<(double)st[i].first/st[i].second<<endl;
        //cout<<"ed"<<' '<<(double)ed[i].first/ed[i].second<<endl;
        ord[i]=i;
        dd[i]=ed[i];
    }
    sort(ord+1,ord+1+n,cmp);
    sort(dd+1,dd+1+n,cmp2);
    for (int i=1;i<=n;i++){
        int lo=1,hi=n+1;
        while (hi-lo>1){
            int mid=lo+hi>>1;
            if (ed[i].first*dd[mid].second>=ed[i].second*dd[mid].first){
                lo=mid;
            }
            else{
                hi=mid;
            }
        }
        id[i]=lo;
    }
    LL ans=0; 
    for (int i=1;i<=n;i++){
        int ii=ord[i];
        ans+=query(n)-query(id[ii]-1);
        add(id[ii]);
    }
    printf("%I64d\n",ans);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值