• LuoguP4704 太极剑


    题面

    测试要求 Bob 尽可能快地切断 n 根绳子。

    所有绳子的端点两两不同,所以共有 2n 个端点。这些端点被捆在一个圆上,等距离分布。我们把这些端点按顺时针方向编号为 1 到 2n。

    Bob 每次切割的轨迹是一条直线,可以将所有与这条直线相交的绳子切断,他想知道至少多少次可以切断所有的绳子。

    (n<=2*10^5)

    传送门

    题解

    先破环成链

    我们可以想到,对于原本环上存在的一条线段,我们可以标记一下它两端点在链上的位置
    而一条直线穿过了此线段,则说明此线段两端点的弧中一定存在一个Bod用太极剑划出的直线的端点

    所以我们只要保证:

    圆上两个标记相同的点之间一定要有一个断点

    考虑如何贪心:

    对于两个标记相同的点,肯定将断点设在靠右的端点上会更优,因为这样能够断开更多其他标记相同的点

    此外我们还需要找到最开始的断点

    (这图我盗的,图片作者抱歉)


    比如上图中,有三个断点,却只需要一条直线
    仔细想想就能知道断点枚举的顺序影响了最终结果

    因为端点之间必定有端点,所以较短的端点距离会比较长的端点距离更好枚举作为断点

    所以我们直接找到最小的端点距离,枚举初始断点

    最后

    我们来想想看它的复杂度,设最小的端点距为d
    所以之后每个端点之间必定会大于间隙d,而断点的选择会是下一个还未被断开的线段的靠右的端点,所以关于找到下一个断点,可以直接在此位置+d,再一一向后枚举
    所以总复杂度为:
    (O(d(枚举初始节点)*(2*n/d)(对于每个初始节点向后递推位置)))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define in inline
    #define ll long long
    #define get getchar()
    in int read()
    {
        int t=0,x=1;char ch=get;
        while((ch<'0'||ch>'9')&&ch!='-')ch=get;
        if(ch=='-')x=-1,ch=get;
        while(ch<='9'&&ch>='0')t=t*10+ch-'0',ch=get;
        return x*t;
    }
    const int _=8e5+5;
    int n;
    struct edge{
        int l,r;
    }a[_];
    int to[_];
    in int dis(int x,int y)
    {
        return min(y-x,2*n-y+x);
    }
    int main()
    {
        n=read();
        int minx=0x3f3f3f3f,xx;
        for(re int i=1;i<=n;i++)
        {
            a[i].l=read(),a[i].r=read();
            if(a[i].l>a[i].r) swap(a[i].l,a[i].r);
            to[a[i].r]=a[i].l;
            to[a[i].l+2*n]=a[i].r;
            to[a[i].r+2*n]=a[i].l+2*n;
            if(minx>dis(a[i].l,a[i].r)){
                minx=dis(a[i].l,a[i].r);
                xx=i;    
            }
        }
        int st,en,ans=0;
        if(a[xx].r-a[xx].l==minx)
        {
            st=a[xx].l,en=a[xx].r;
        }
        else en=a[xx].l+2*n,st=a[xx].r;
        int len=dis(a[xx].l,a[xx].r);
        ans=0x3f3f3f3f;
        for(re int i=st;i<=en;i++)
        {
            int last=i,sum=0;
            for(re int j=i+len;j<i+2*n;j++)
                if(to[j]>last) sum++,last=j,j+=len;
            ans=min(ans,sum);
        }
        cout<<(ans)/2+1<<endl;
        return 0;
    }
    
    
  • 相关阅读:
    高质量动漫实时画质增强器Anime4K在mpv上的配置
    grep中正则表达式使用尖括号表示一个单词
    虚拟机复制的linux无法联网,解决Bringing up interface eth0: Device eth0 does not seem to be present, delaying initialization.
    Linux将动态IP改为静态IP
    回车、换行的区别
    栈的链接存储
    栈的顺序存储
    冒泡排序
    插入排序
    双向循环链表
  • 原文地址:https://www.cnblogs.com/yzhx/p/12723430.html
走看看 - 开发者的网上家园