bzoj4082
贪心+倍增
首先如果这个问题在序列上,好像可以按右端点排序,然后从起点开始向能到的最远的地方走。
但是环上不可以,因为随即一个起点可能不是最小的。
然后神思路来了:我们先将环展开倍增,再将区间按右端点排序,然后每个区间连向能达到最远的区间连边。因为每个区间只向外连一条边,而且最后一个区间没有后继,所以这是一颗树。(森林不可能吧,因为每个点都被覆盖了,那么每个区间都有后继,到达最后的区间时肯定有会归到最后一个区间)枚举每个区间,向上倍增,因为每个区间的祖先右端点都要大一些,所以只要倍增到自己左端点时就是答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }