[GX/GZOI2019]特技飞行(扫描线+置换)

感觉是6题中最难的一题,其实这题是一个二合一:

第一问:给定平面上若干点和k个关键点,关键点覆盖一个45°倾斜的正方形范围r,求有多少点被至少一个关键点覆盖。这个可以曼哈顿转切比雪夫距离,然后再扫描线求解,复杂度O(nlogn)

第二问:求最少和最多有多少次擦肩而过。显然每个交点都可以做对向交换,这是最少擦肩而过的次数。最多的次数,假设全部擦肩而过得到排列p,对向交换实际上是交换两元素位置,用最小交换次数还原排列即可。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>pii;
const int N=5e5+7;
int n,m,a,b,c,x0,x1,ans1,ans2,tot,cnt,ans,C[N<<1],y[N][2],f[N],vis[N];
double d[N<<1];
set<pii>S;
struct node{double x,y;int v,p;}p[N<<1];
bool operator<(node a,node b){return a.x<b.x;}
node calc(int i,int j)
{
    int y1=y[j][0]-y[i][0],y2=y[i][1]-y[j][1];
    double k=1.0*y1/(y1+y2),xl=1.0*x0+k*(x1-x0),yl=1.0*y[i][0]+k*(y[i][1]-y[i][0]);
    d[++cnt]=xl-yl;
    return(node){xl+yl,xl-yl,0,0};
}
bool cmp(node a,node b){return fabs(a.x-b.x)<1e-10?a.y<b.y:a.x<b.x;}
void add(int x,int v){while(x<=cnt)C[x]+=v,x+=x&-x;}
int query(int x){int ret=0;while(x)ret+=C[x],x-=x&-x;return ret;}
bool cmp1(int a,int b){return y[a][1]<y[b][1];}
int main()
{
    scanf("%d%d%d%d%d%d",&n,&a,&b,&c,&x0,&x1);
    for(int i=1;i<=n;i++)scanf("%d",&y[i][0]);
    for(int i=1;i<=n;i++)scanf("%d",&y[i][1]);
    for(int i=1;i<=n;i++)
    {
        pii u=make_pair(y[i][1],i);
        set<pii>::iterator it=S.lower_bound(u);
        while(it!=S.end())p[++tot]=calc(it->second,i),it++;
        S.insert(u);
    }
    int sum=tot;scanf("%d",&m);
    for(int i=1,x,y,z;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        d[++cnt]=x-y+z,d[++cnt]=x-y-z;
        p[++tot]=(node){x+y+z,x-y+z,1,0};
        p[++tot]=(node){x+y+z,x-y-z,-1,0};
        p[++tot]=(node){x+y-z,x-y+z,-1,0};
        p[++tot]=(node){x+y-z,x-y-z,1,0};
    }
    sort(p+1,p+tot+1);
    sort(d+1,d+cnt+1);
    cnt=unique(d+1,d+cnt+1)-d-1;
    for(int i=1;i<=tot;i++)p[i].p=upper_bound(d+1,d+cnt+1,p[i].y-1e-10)-d;
    sort(p+1,p+tot+1,cmp);
    for(int i=1;i<=tot;i++)if(p[i].v)add(p[i].p,p[i].v);else ans+=query(p[i].p)>0;
    ans1=ans*c+sum*a;
    for(int i=1;i<=n;i++)f[i]=i;
    sort(f+1,f+n+1,cmp1);
    int num=n;
    for(int i=1;i<=n;i++)
    if(!vis[i])
    {
        num--;
        for(int j=i;!vis[j];j=f[j])vis[j]=1;
    }
    ans2=ans1+(b-a)*(sum-num);
    if(ans1>ans2)swap(ans1,ans2);
    printf("%d %d",ans1,ans2);
}

原文地址:https://www.cnblogs.com/hfctf0210/p/10859242.html

时间: 2024-11-10 00:45:53

[GX/GZOI2019]特技飞行(扫描线+置换)的相关文章

题解-GXOI/GZOI2019 特技飞行

Problem loj3085 bzoj不放题面差评 题意概要:给出两条竖直直线,再给出 \(n\) 架飞机的初始航线:一条接通这两条直线的线段,保证航线交点不在两条直线上.现要求安排所有飞机在航线相交处做特技: 擦身而过:两架飞机按原方向线路继续前进,一次得分 \(b\) 对向交换:两架飞机交换线路继续前进,一次得分 \(a\) 另外,给定 \(k\) 个边界与坐标轴成 \(45°\)角 的正方形,若一次特技被至少一个正方形囊括,则总得分加 \(c\) 现要求决策每次相遇做的特技,求最大/最小

Loj #3085. 「GXOI / GZOI2019」特技飞行

Loj #3085. 「GXOI / GZOI2019」特技飞行 题目描述 公元 \(9012\) 年,Z 市的航空基地计划举行一场特技飞行表演.表演的场地可以看作一个二维平面直角坐标系,其中横坐标代表着水平位置,纵坐标代表着飞行高度. 在最初的计划中,这 \(n\) 架飞机首先会飞行到起点 \(x = x_{st}\) 处,其中第 \(i\) 架飞机在起点处的高度为 \(y_{i,0}\).它们的目标是终点 \(x = x_{ed}\) 处,其中第 \(i\) 架飞机在终点处的高度应为 \(y

2697: 特技飞行

2697: 特技飞行 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 646  Solved: 393[Submit][Status] Description 神犇航空开展了一项载客特技飞行业务.每次飞行长N个单位时间,每个单位时间可以进行一项特技动作,可选的动作有K种,每种动作有一个刺激程度Ci.如果连续进行相同的动作,乘客会感到厌倦,所以定义某次动作的价值为(距上次该动作的时间)*Ci,若为第一次进行该动作,价值为0.安排一种方案,使得总价值最

bzoj2697特技飞行*

bzoj2697特技飞行 题意: N个单位时间,每个单位时间可以进行一项特技动作,可选的动作有K种,每种动作有一个刺激程度Ci.每次动作的价值为(距上次该动作的时间)*Ci,若为第一次进行该动作,价值为0.求最大总价值.N≤1000,K≤300. 题解: 因为如果同个动作做3次,不如只做头尾两次更好.所以把动作按Ci降序排序,把Ci大的尽量放在两端. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algori

BZOJ 2697 特技飞行(贪心)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2697 [题目大意] 神犇航空开展了一项载客特技飞行业务. 每次飞行长N个单位时间,每个单位时间可以进行一项特技动作, 可选的动作有K种,每种动作有一个刺激程度Ci. 如果连续进行相同的动作,乘客会感到厌倦, 所以定义某次动作的价值为(距上次该动作的时间)*Ci,若为第一次进行该动作,价值为0. 安排一种方案,使得总价值最大. [题解] 我们发现一个动作进行两遍收益才是最大的,进行第三

BZOJ2697: 特技飞行

2697: 特技飞行 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 607  Solved: 363[Submit][Status] Description 神 犇航空开展了一项载客特技飞行业务.每次飞行长N个单位时间,每个单位时间可以进行一项特技动作,可选的动作有K种,每种动作有一个刺激程度Ci.如果连 续进行相同的动作,乘客会感到厌倦,所以定义某次动作的价值为(距上次该动作的时间)*Ci,若为第一次进行该动作,价值为0.安排一种方案,使得总价

[GX/GZOI2019]旅行者(dijkstra)

二进制分组SB做法没意思还难写还可能会被卡常其实是我不会写.用一种比较优秀的O(Tnlogn)做法,只需要做2次dijkstra.对原图做一次.对反图做一次,然后记录每个点的最短路是从k个源点中的哪个转移过来的.然后枚举每条边,若两边转移过来的源点不同,则用d1+w[i]+d2来更新答案即可. #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+7,M=5e5+7; struct

[GX/GZOI2019]旧词(树上差分+树剖+线段树)

考虑k=1的做法:这是一道原题,我还写过题解,其实挺水的,但当时我菜还是看题解的:https://www.cnblogs.com/hfctf0210/p/10187947.html.其实就是树上差分后值为1. 考虑k>1的做法:其实可以再次树上差分,给每个点i赋值v[i]=dep[i]k-dep[i-1]k,然后还是和原来一样开一棵线段树,记录一个val[rt]表示当前节点内区间v值的和,以及sum[rt]表示区间值.修改时打标记,只需要将sum[rt]+=v*val[rt],lazy[rt]+

基于unity的飞行模拟设计

使用unity开发游戏真是非常方便.研究飞行模拟也有一段时日,尝试过物理和数学模拟.从效果上来看,物理模拟较为真实一点.但是操作不好.数学模拟的话,虽然牺牲了飞行效果,操控是非常方便的. 所谓的数学模拟,就是位移模拟,通过定义起飞速度,加速度等,模拟飞机的飞行过程,包括转向,飞行坠落等. 来看一下飞机的飞行状态:在地面上,飞机达到起飞速度时,可以拉起飞机,否则,一直在地面上:在空中:当飞机低于起飞速度,下降.大于起飞速度则能保持在空中.飞机不可能倒着飞行的,所以,飞机的速度状态就有:起飞速度,正