【BZOJ-4082】Surveillance 树链剖分 LCA + 贪心

4082: [Wf2014]Surveillance

Time Limit: 40 Sec  Memory Limit: 128 MB
Submit: 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

Sample Output

3

HINT

Source

鸣谢qpswwww提供译文

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;
}

 

转载于:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5917689.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值