HDU 6135 Casual Podracing(CDQ分治)

26 篇文章 0 订阅

Description

一个周长为 L 的圆弧上面有n个不重合的点,第 i 个点距圆弧起点距离(圆上距离)为di,速度为 vi (取逆时针为正方向,任意两点速度不同),能量为 wi ,一个点会被标记当且仅当其在运动过程中与能量值大于其能量值的点相遇(一个点会被标记后会继续运动),问从什么时刻开始不会再有点会被标记

Input

第一行一整数 T 表示用例组数,每组用例首先输入两个整数n L 表示圆上点数和圆周长,之后输出n个整数 di 表示每个点的位置,然后输入 n 个整数vi表示每个点的速度,最后输入 n 个整数wi表示每个点的能量值

(T20,1L106,0di<L,|vi|106,0wi<n,n105)

Output

对于每组用例,输出最后一个点被标记的时刻的最简分数形式

Sample Input

1
8 8
2 1 5 7 4 6 0 3
86 27 -85 -8 11 73 -37 -69
0 6 4 4 3 0 2 5

Sample Output

4/61

Solution

对于一个时间 t ,假设di<dj,那么第 i 个点和第j个点在 0 ~t时间段相遇等价于 di+vitdj+vjt di+vitdj+vjtL

二分 t ,对于一个t,用 CDQ 分治求出 0 ~t时间段内被标记的点,首先把所有点按 d 值升序排序,求出每个点d+vt的值记为 val ,在分治过程中对 w 归并排序使得w降序

对于区间 [l,r] ,假设已经解决 [l,mid] [mid+1,r] ,那么合并过程中需要知道两个区间的互相影响,在归并过程中,维护左区间 val 的最值 tlmin,tlmax 和右区间 val 的最值 trmin,trmax 以及左右区间能量值小于当前插入点能量值的点的最值 lmin,lmax,rmin,rmax ,每次比较两个区间前端元素 i j,对于要插入到合并后序列的点,如果是 i ,其属于左区间,那么只要合并后区间存在k满足相遇条件 valivalk valivalkL ,那么 i 就会被标记,此条件等价于valirmin valirmaxL ,同理如果是 j ,那么只要合并后区间存在k满足相遇条件 valkvalj valkvaljL ,那么 j 就会被标记,此条件等价于lmaxvalj lmin+Lvalj ,插入后注意维护左右区间最值

对于一个时间 t ,如果分治结束后,存在一个点未被标记且其能量值不是最大值,由于任意两点速度不同,那么这个点之后还可以被标记,说明该二分值过小,否则说明二分值满足条件,还可以寻找更小的答案

由于答案要输出分数形式,考虑最后一个被标记的点last,在分治过程中只要存在能量值不是最大值且未被标记的点,说明该点未被标记,将其赋给 last 即可,最后考虑所有能量值大于 last 的点,求出这些点与 last 相遇时间的最小值即为答案

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define maxn 100005
#define INF 1e20
#define eps 1e-6
struct node
{
    int d,v,w,id;
    double val;
    bool operator<(const node &b)const
    {
        return d<b.d;
    }
}p[maxn],a[maxn],temp[maxn];
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
int T,n,L,Maxw,last,vis[maxn];
void CDQ(int l,int r)
{
    if(l>=r)return ;
    int mid=(l+r)/2;
    CDQ(l,mid),CDQ(mid+1,r);
    int i=l,j=mid+1,k=l;
    double tlmin=INF,tlmax=-INF,trmin=INF,trmax=-INF;
    double lmin=INF,lmax=-INF,rmin=INF,rmax=-INF;
    while(i<=mid|| j<=r)
    {
        if(j==r+1||i<=mid&&a[i].w>a[j].w)
        {
            if(k>l&&temp[k-1].w!=a[i].w)
                lmin=tlmin,lmax=tlmax,rmin=trmin,rmax=trmax;
            if(a[i].val>=rmin||a[i].val<=rmax-L)vis[a[i].id]=1;
            tlmin=min(tlmin,a[i].val),tlmax=max(tlmax,a[i].val);
            temp[k++]=a[i++];
        }   
        else
        {
            if(k>l&&temp[k-1].w!=a[j].w)
                lmin=tlmin,lmax=tlmax,rmin=trmin,rmax=trmax;
            if(a[j].val<=lmax||a[j].val>=lmin+L)vis[a[j].id]=1;
            trmin=min(trmin,a[j].val),trmax=max(trmax,a[j].val);
            temp[k++]=a[j++];
        }
    }
    for(int i=l;i<=r;i++)a[i]=temp[i];
}
bool check(double t)
{
    for(int i=1;i<=n;i++)a[i]=p[i],a[i].val=a[i].d+t*a[i].v,vis[i]=0;
    CDQ(1,n);
    for(int i=1;i<=n;i++)
        if(!vis[i]&&p[i].w!=Maxw)
        {
            last=i;
            return 0;
        }
    return 1;
}
void update(node a,node b,int &x,int &y)
{
    if(a.v<b.v)swap(a,b);
    int tx,ty=a.v-b.v;
    if(a.d<b.d)tx=b.d-a.d;
    else tx=L+b.d-a.d;
    if(1ll*tx*y<=1ll*x*ty)x=tx,y=ty;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&L);
        for(int i=1;i<=n;i++)scanf("%d",&p[i].d);
        for(int i=1;i<=n;i++)scanf("%d",&p[i].v);
        for(int i=1;i<=n;i++)scanf("%d",&p[i].w);
        sort(p+1,p+n+1);
        Maxw=p[1].w;
        for(int i=2;i<=n;i++)Maxw=max(Maxw,p[i].w);
        for(int i=1;i<=n;i++)p[i].id=i;
        double l=0,r=L,mid;
        last=0;
        while(l<r-eps)
        {
            mid=0.5*(l+r);
            if(check(mid))r=mid;
            else l=mid;
        }
        if(!last)printf("0\n");
        else
        {
            int x=0,y=0;
            for(int i=1;i<=n;i++)
                if(p[i].w>p[last].w)update(p[i],p[last],x,y);
            int g=gcd(x,y);
            printf("%d/%d\n",x/g,y/g);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值