Luogu P5302 [GXOI/GZOI2019]特技飞行

一道码量还算中等的三合一,主要还是套路为主(然后我写到一半把代码删了233)

首先我们很容易看出来不管改不改变航向,所有的交点位置都是确定的

而交点意味着什么?逆序对!所以我们就不会奇怪数据范围中给出了交点个数的限制了

因此我们就有一个很naive的想法,直接暴力归并排序找出所有的逆序对,然后运用一下简单的计算几何知识(其实就是求直线交点)来搞出所有交点

然后下一部分也很清晰了,直接把固定的加分算出来,这个一眼矩阵数点啊,不强制在线的话离线+扫描线+树状数组就做完了啊

慢着,这矩阵怎么是斜的233,其实没事,我们把所有点都翻转\(\frac{\pi}{4}\)即可

还有一点就是求出的交点都是实数,所以一定要离散化

然后剩下的就是这道题最不套路的地方了,我们假设一共有\(cnt\)个交点,然后我们人为决定进行\(t\)次交换航线,因此这部分的贡献就是\(a\cdot t+b\cdot (cnt-t)=(a-b)\cdot t+b\cdot cnt\)

发现是个关于\(t\)的一次函数,说明我们只要找出\(t\)最大最小值就可以得出答案

首先最大值很好求,我们根据交点个数=逆序对个数以及逆序对的定义,很容易发现最多时全部交换即可,因此\(t_{\max}=cnt\)

然后我们考虑最后的序列是一个排列,而让一个排列有序的最少步数就是排列长度-置换个数

为什么的?因为每一个置换想要有序至少要交换置换长度-1次(每次只能让一个数归位,最后一次让两个数归位),因此就可以推出上面的式子了

所以这道题就大致写完了,最后提醒一下各位:调代码时时刻注意存档!(被辣鸡Devcpp把代码格没了的蒟蒻默默发声)

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
typedef double DB;
const int N=1e5+5,M=5e5+5;
const DB EPS=1e-8;
struct Point
{
    DB x,y;
    inline Point(const DB& X=0,const DB& Y=0)
    {
        x=X; y=Y;
    }
}P,Q;
inline Point trans(const Point& P)
{
    return Point(P.x+P.y,P.y-P.x);
}
struct line
{
    int y1,y2;
}l[N]; int n,a,b,c,x1,x2,k;
struct data
{
    int val,id;
}s[N];
inline int dcmp(const DB& x)
{
    if (fabs(x)<EPS) return 0; return x<0?-1:1;
}
struct DB_val
{
    DB val;
    friend bool inline operator < (const DB_val& A,const DB_val& B)
    {
        return dcmp(A.val-B.val)<0;
    }
    friend bool inline operator == (const DB_val& A,const DB_val& B)
    {
        return !dcmp(A.val-B.val);
    }
};
struct event
{
    int opt; DB x,y1,y2; //opt: 1:add line; -1:del line; 0:query point;
}et[(N<<1)+M]; int cnt,ret,p,q,r,mi,num;
struct real_event
{
    int opt,x,y1,y2;
    friend bool operator < (const real_event& A,const real_event& B)
    {
        return A.x<B.x;
    }
}rt[(N<<1)+M];
class FileInputOutput
{
    private:
        static const int S=1<<21;
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
        char Fin[S],*A,*B;
    public:
        Tp inline void read(T& x)
        {
            x=0; char ch; while (!isdigit(ch=tc()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
        }
        #undef tc
}F;
namespace Step1 //Find out all intersections
{
    data tp[N]; Point cp[M];
    inline Point cross(CI p,CI q)
    {
        DB k1=1.0*(l[p].y2-l[p].y1)/(x2-x1),k2=1.0*(l[q].y2-l[q].y1)/(x2-x1);
        DB b1=l[p].y1-k1*x1,b2=l[q].y1-k2*x1,x=(b2-b1)/(k1-k2),y=k1*x+b1; return Point(x,y);
    }
    inline void divide(CI l=1,CI r=n)
    {
        if (l>=r) return; int mid=l+r>>1; divide(l,mid); divide(mid+1,r);
        RI i=l,j=mid+1,k=l; while (i<=mid&&j<=r) if (s[i].val<s[j].val) tp[k++]=s[i++];
        else { for (RI p=i;p<=mid;++p) cp[++cnt]=cross(s[p].id,s[j].id); tp[k++]=s[j++]; }
        while (i<=mid) tp[k++]=s[i++]; while (j<=r) tp[k++]=s[j++]; for (i=l;i<=r;++i) s[i]=tp[i];
    }
    inline void solve(void)
    {
        divide(); for (RI i=1;i<=cnt;++i) cp[i]=trans(cp[i]),et[i]=(event){0,cp[i].x,cp[i].y,0};
        //for (RI i=1;i<=cnt;++i) printf("%.2lf %.2lf\n",cp[i].x,cp[i].y);
    }
};
namespace Step2 //Point in rectangles identify
{
    DB_val rst[((N<<1)+M)<<1]; int tot;
    class Tree_Array
    {
        private:
            int bit[(N<<1)+M];
            #define lowbit(x) x&-x
        public:
            inline void add(RI x,CI y)
            {
                for (;x<=tot;x+=lowbit(x)) bit[x]+=y;
            }
            inline int get(RI x,int ret=0)
            {
                for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;
            }
            #undef lowbit
    }BIT;
    inline int find(const DB& x)
    {
        return lower_bound(rst+1,rst+tot+1,(DB_val){x})-rst;
    }
    inline void solve(void)
    {
        RI i; for (i=1;i<=cnt;++i) rst[i].val=et[i].x; sort(rst+1,rst+cnt+1);
        for (tot=unique(rst+1,rst+cnt+1)-rst-1,i=1;i<=cnt;++i) rt[i].x=find(et[i].x);
        for (tot=0,i=1;i<=cnt;++i) rst[++tot].val=et[i].y1,rst[++tot].val=et[i].y2;
        sort(rst+1,rst+tot+1); tot=unique(rst+1,rst+tot+1)-rst-1;
        for (i=1;i<=cnt;++i) rt[i].opt=et[i].opt,rt[i].y1=find(et[i].y1),rt[i].y2=find(et[i].y2);
        for (sort(rt+1,rt+cnt+1),i=1;i<=cnt;++i) switch (rt[i].opt)
        {
            case 0:
                if (BIT.get(rt[i].y1)) ++ret; break;
            case 1:
                BIT.add(rt[i].y1,1); BIT.add(rt[i].y2+1,-1); break;
            case -1:
                BIT.add(rt[i].y1,-1); BIT.add(rt[i].y2+1,1); break;
        }
    }
};
namespace Step3 //Actions at intersections
{
    int cur; bool vis[N];
    inline int findcircle(void)
    {
        for (RI i=1,j;i<=n;++i) if (!vis[j=s[i].id])
        for (vis[j]=1,++cur,j=s[j].id;j!=s[i].id;j=s[j].id)
        vis[j]=1; return num-(n-cur);
    }
};
inline void swap(int& x,int& y)
{
    int t=x; x=y; y=t;
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    F.read(n); F.read(a); F.read(b); F.read(c); F.read(x1); F.read(x2);
    RI i; for (i=1;i<=n;++i) s[i].id=i,F.read(l[i].y1);
    for (i=1;i<=n;++i) F.read(s[i].val),l[i].y2=s[i].val;
    for (Step1::solve(),num=cnt,F.read(k),i=1;i<=k;++i)
    {
        F.read(p); F.read(q); F.read(r); P=trans(Point(p-r,q)); Q=trans(Point(p+r,q));
        et[++cnt]=(event){1,P.x,Q.y,P.y}; et[++cnt]=(event){-1,Q.x+EPS*10,Q.y,P.y};
    }
    Step2::solve(); int ans1=a*num,ans2=ans1; ans2+=(b-a)*Step3::findcircle();
    if (ans1>ans2) swap(ans1,ans2); return printf("%d %d",ans1+c*ret,ans2+c*ret),0;
}
posted @ 2019-06-26 21:31 hl666 阅读(...) 评论(...) 编辑 收藏