bzoj4082

贪心+倍增

首先如果这个问题在序列上,好像可以按右端点排序,然后从起点开始向能到的最远的地方走。

但是环上不可以,因为随即一个起点可能不是最小的。

然后神思路来了:我们先将环展开倍增,再将区间按右端点排序,然后每个区间连向能达到最远的区间连边。因为每个区间只向外连一条边,而且最后一个区间没有后继,所以这是一颗树。(森林不可能吧,因为每个点都被覆盖了,那么每个区间都有后继,到达最后的区间时肯定有会归到最后一个区间)枚举每个区间,向上倍增,因为每个区间的祖先右端点都要大一些,所以只要倍增到自己左端点时就是答案。

#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
struct cover {
    int x, y;
} a[N];
int n, len, ans = 1 << 29;
int fa[N][23], dep[N], mn[N];
bool cp(cover x, cover y)
{
    return x.y < y.y;
}
int query(int x, int limit)
{
    for(int i = 22; i >= 0; --i) 
        if(fa[x][i] != -1 && a[fa[x][i]].y < limit)
            x = fa[x][i];
    return a[x].y >= limit ? x : fa[x][0];
}
void getdep(int x)
{
    if(dep[x]) 
        return;
    if(fa[x][0] == -1)
    {
        dep[x] = 1;
        return;
    }
    getdep(fa[x][0]);
    dep[x] = dep[fa[x][0]] + 1;
}
int main()
{
    scanf("%d%d", &len, &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d%d", &a[i].x, &a[i].y);
        if(a[i].y < a[i].x) 
            a[i].y += len;
    }
    sort(a + 1, a + n + 1, cp);
    int j = 1;
    memset(fa, -1, sizeof(fa));
    memset(mn, 0x3f3f, sizeof(mn));
    for(int i = n; i; --i)
        mn[i] = min(mn[i + 1], a[i].x);
    for(int i = 1; i <= n; ++i)
    {
        while(a[i].y >= mn[j + 1] - 1 && j < n) 
            ++j;
        fa[i][0] = j == i ? -1 : j;
    }
    for(int i = 1; i <= n; ++i)
        getdep(i);
    for(int k = 1; k <= 22; ++k)
        for(int i = 1; i <= n; ++i)
            if(fa[i][k - 1] != -1)
                fa[i][k] = fa[fa[i][k - 1]][k - 1];
    for(int i = 1; i <= n; ++i)
    {
        int x = query(i, a[i].x + len - 1);
        if(x != -1) 
            ans = min(ans, dep[i] - dep[x] + 1);
    }
    if(ans != 1 << 29)
        printf("%d\n", ans);
    else
        puts("impossible");
    return 0;
}
View Code

 

posted @ 2017-06-22 17:07  19992147  阅读(250)  评论(0编辑  收藏  举报