[BZOJ 4082] Surveillance
Link:
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; }