[BZOJ 4082] Surveillance

Link:

BZOJ 4082 传送门

Solution:

对于链上这样的问题贪心就好了

如果在一个环上,肯定需要将环转化成链,$O(n)$确定起点才能计算

但枚举每个节点拆环再贪心的复杂度为$O(n^2)$,明显会超时

于是我们要将已知起点,计算从起点走完一圈的距离的时间复杂度降到$log(n)$

 

这时联想到倍增算法:

将所有区间按照右端点排序后,将每个区间和其能达到的右端点最远的区间相连

可以发现,这样就形成了一个$DAG$(每个点都只会向后连边)

接下来只要对于每个点在$DAG$上倍增即可,计算当右端点回到该点起点时的距离

 

Tip:$BZOJ$的题面上没写如无解则输出$impossible$

Code:

#include <bits/stdc++.h>

using namespace std;
typedef pair<int,int> P;
#define X first
#define Y second
const int MAXN=1e6+10,INF=1<<27;
P dat[MAXN];
int l,n,f[MAXN][25],mn[MAXN],res;

bool cmp(P a,P b){return a.Y<b.Y;}

int main()
{
    scanf("%d%d",&l,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&dat[i].X,&dat[i].Y);
        if(dat[i].Y<dat[i].X) dat[i].Y+=l;
    }
    sort(dat+1,dat+n+1,cmp);
    
    mn[n+1]=INF;res=INF;
    for(int i=n;i>=1;i--) mn[i]=min(mn[i+1],dat[i].X);
    int cur=1;
    for(int i=1;i<=n;i++)
    {
        while(cur<n && mn[cur+1]-1<=dat[i].Y) cur++;
        if(cur!=i) f[i][0]=cur;
    }
    for(int i=n;i>=1;i--)
        for(int j=1;j<=20;j++)
            f[i][j]=f[f[i][j-1]][j-1];
    
    for(int i=1;i<=n;i++)
    {
        int cur=i,cnt=1;
        for(int j=20;j>=0;j--)
            if(f[cur][j] && dat[f[cur][j]].Y<dat[i].X+l-1) cur=f[cur][j],cnt+=1<<j;
        if(f[cur][0] && dat[cur].Y<dat[i].X+l-1) cur=f[cur][0],cnt++;
        if(dat[cur].Y>=dat[i].X+l-1) res=min(res,cnt);
    }
    if(res==INF) puts("impossible");
    else printf("%d\n",res);
    return 0;
}

 

posted @ 2018-06-29 08:57  NewErA  阅读(206)  评论(0编辑  收藏  举报