【BZOJ-4082】Surveillance 树链剖分 LCA + 贪心
4082: [Wf2014]Surveillance
Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 260 Solved: 100
[Submit][Status][Discuss]
Description
给你一个长度为len的环,以及n个区间,要你选择尽量少的区间,使得它们完全覆盖整个环。问最少要多少个区间。
Input
输入数据的第一行是两个整数len和n,代表环的长度以及区间个数。之后n行描述的是n个区间,每个区间分别用一对数字(a,b)表示,若a≤b则表示这个区间覆盖的是[a,b]部分,否则表示这个区间覆盖的是除掉[a+1,b-1]以外的其他部分。
Output
输出只有一行,一个整数,代表覆盖整个环所需要的最少区间个数。
Sample Input
100 7
1 50
50 70
70 90
90 40
20 60
60 80
80 20
1 50
50 70
70 90
90 40
20 60
60 80
80 20
Sample Output
3
HINT
Source
Solution
这个题还是很巧妙的啊!
首先这个题有个序列上的版本在CodeVS,那样只需要贪心的排序,然后从头开始每次选一个覆盖最长的即可。
但是环上的情况显然不能这么搞,因为可以从每个点开始搞,都会有不同的方法。
但是这个思路还是可以利用的,我们展环成链,还是贪心的按照右端从小到大排序。
序列上的版本,我们是直接从起始节点开始每次都选一个覆盖最长的,那这里同样,只有全部是覆盖最长的才有可能是最优的。
那么我们对每个节点向它覆盖最长的连边,这样会形成一棵树,可以发现,这样树上两点的距离就是答案。
所以我们求一下LCA,枚举一下每个点做起始时的答案,取一下min即可。
还是有很多细节需要处理的,比如我们很有可能会覆盖过多,所以最后并不是每次查$i->i+L$,所以我们需要一开始用一个set维护一下这个东西。
这样总的复杂度大概是$O(NlogN)$,范围大概是$10^{6}$的,有点虚所以写的链剖
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<set> using namespace std; #define INF 0x7fffffff inline int read() { int x=0; char ch=getchar(); while (ch<'0' || ch>'9') {ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x; } #define MAXN 1000010 struct SecNode { int s,t; bool operator < (const SecNode & A) const {return t==A.t? s<A.s:t<A.t;} }sec[MAXN]; struct EdgeNode{int next,to;}edge[MAXN<<2]; int head[MAXN<<1],cnt=1; inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;} inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);} int size[MAXN<<1],fa[MAXN<<1],deep[MAXN<<1],son[MAXN<<1],top[MAXN<<1]; bool ok[MAXN<<1]; int N,L,ans=INF; set<int>st; inline void DFS_1(int now) { if (deep[now]) return; if (!fa[now]) { deep[now]=1; if (now>L && ok[now]) fa[now]=2*L+1,InsertEdge(fa[now],now); return; } DFS_1(fa[now]); InsertEdge(fa[now],now); deep[now]=deep[fa[now]]+1; } inline void DFS_2(int now) { size[now]=1; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=fa[now]) { DFS_2(edge[i].to); size[now]+=size[edge[i].to]; if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to; } } inline void DFS_3(int now,int chain) { top[now]=chain; if (son[now]) DFS_3(son[now],chain); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=son[now] && edge[i].to!=fa[now]) DFS_3(edge[i].to,edge[i].to); } inline int LCA(int u,int v) { v=*st.lower_bound(v); if (!top[u] || !top[v]) return -1; while (top[u]!=top[v]) { if (deep[top[u]]<deep[top[v]]) swap(u,v); u=fa[top[u]]; } if (deep[u]>deep[v]) swap(u,v); return u; } int main() { L=read(),N=read(); for (int i=1; i<=N; i++) sec[i].s=read(),sec[i].t=read(),sec[i].t=sec[i].t<sec[i].s? sec[i].t+L:sec[i].t; sort(sec+1,sec+N+1); for (int i=N; i; i--) if (!fa[sec[i].s]) for (int j=sec[i].s; j<=L && j<=sec[i].t && !fa[j]; j++) {ok[ fa[j]=sec[i].t+1 ]=1; if (fa[j]>L) st.insert(fa[j]);} st.insert(2*L+2); for (int i=1; i<=L*2; i++) DFS_1(i); DFS_2(2*L+1); DFS_3(2*L+1,2*L+1); for (int lca,i=1; i<=L; i++) lca=LCA(i,i+L),ans=lca!=-1? min(ans,deep[i]-deep[lca]):ans; if (ans==INF) puts("impossible"); else printf("%d\n",ans); return 0; }
——It's a lonely path. Don't make it any lonelier than it has to be.