HDU 6203 ping ping ping LCA+dfs序+树状数组

http://acm.hdu.edu.cn/showproblem.php?pid=6203
The structure of the computer room in Northeastern University is pretty miraculous. There are n servers, some servers connect to the gateway whose IP address is 0 directly. All servers are connected with each other by n netting twines. It is said that this structure is favorable for maintaining physical problem of servers.
But because of an unexpected rainstorm, the computer room was destroyed by a terrible thunderclap!
Our maintainer Bittersweet found that many servers were not able to be visited, so he hurried to the computer room to lookup the reason. After several hours, Bittersweet realized that some net gape of servers were broken by thunderclap. However, there were too many servers to find out all the broken net gapes quickly. So he came up with an idea to assess the damaged condition roughly. Bittersweet decided to turn on some servers and ping other servers randomly, then record the unsuccessful pairs of servers.
Now he need a program to analyze the record to confirm what is the minimum
number of servers whose net gape was destroyed by thunderclap. Can you help him to complete this work?
Input
There are at most 20 test cases.
In each test case, the first line is an integer n (3≤n≤104), denoting the number of servers. The IP address of these servers is 1…n.
Then follows n lines, each line contains two integers u and v (0≤u,v≤n), denoting that the server whose IP address is u is connected with the server whose IP address is v by netting twine initially.
After those, there is one line contains only an integer p (p≤50000), denoting the number that Bittersweet uses ping.
Then follows p lines, each line contains two integers U and V , denoting when using server U to ping server V
, it returned unsuccessful.
Output
A single integer x in a line, denoting at least x
servers whose net gape were broken.
Sample Input

4
1 0
4 2
2 0
3 2
2
1 3
2 1

Sample Output

1

题目大意:现有n+1个点和n条边的树,给出n条边的连通关系,现有m条信息,每条信息有两个点u、v,表示从u和v不连通了。问最少从树上删去哪几个点可以满足上述的m条信息。
思路:仔细思考不难知道,如果点v被删去了,那么从v的子树到其他点必定是不连通的。除非这个点也是v的子树节点,但是如果我们从深度大的开始处理不就没有这种情况了吗。因此首先明确,我们要按照深度从大到小来处理,那么按照什么深度呢,当然是u、v节点的LCA的深度。因为按照贪心策略最优的决策就是删去u、v的LCA,因为这样可能涵盖到更多的点。那么大体思路就有了:读入m条信息计算LCA并按照LCA的深度从大到小排序。现在问题的关键是在删除操作之后怎么维护信息。我们考虑用dfs序+树状数组。前面已经说过删去节点grand就意味着从grand子树爬上来的节点一定是不连通的,而dfs序恰恰就维护了一个节点及其子树的连续区间,我们用树状数组给这个区间打上标记就好了: a d d ( i n [ g r a n d ] , 1 ) a d d ( o u t [ g r a n d ] + 1 , − 1 ) add(in[grand],1)\quad add(out[grand]+1,-1) add(in[grand],1)add(out[grand]+1,1)判断当前u、v的LCA是否需要删去的操作也很简单,即判断: s u m ( i n [ u ] )   ∣ ∣   s u m ( i n [ v ] ) sum(in[u])\ ||\ sum(in[v]) sum(in[u])  sum(in[v])

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

const int maxn=1e4+5;

struct edge
{
    int to,nxt;
};

struct q
{
    int u,v,grand,deep;
    bool operator <(const q &a)const
    {
        return deep>a.deep;
    }
}query[50005];

edge Edge[maxn<<1];
int in[maxn];
int out[maxn];
int head[maxn];
int fa[maxn];//父节点
int deep[maxn];//深度
int n,m,cnt=0,tot=0;
int f[maxn][30];
int tree[maxn];

inline int lowbit(int x)
{
    return x&(-x);
}

inline void add(int i,int v)
{
    for(;i<=n;i+=lowbit(i))
        tree[i]+=v;
}

inline int sum(int i)
{
    int ans=0;
    for(;i;i-=lowbit(i))
        ans+=tree[i];
    return ans;
}

inline void addedge(int u,int v)
{
    Edge[++cnt].to=v,Edge[cnt].nxt=head[u],head[u]=cnt;
    Edge[++cnt].to=u,Edge[cnt].nxt=head[v],head[v]=cnt;
}

void dfs(int u,int father)
{
    in[u]=++tot;
    deep[u]=deep[father]+1;
    f[u][0]=father;
    for(int i=1;i<=20;i++)
	f[u][i]=f[f[u][i-1]][i-1];
    for(int i=head[u];i;i=Edge[i].nxt)
        if(Edge[i].to!=father)
            dfs(Edge[i].to,u);
    out[u]=tot;
}

inline int skip(int x,int level)
{
    for(int i=20;i>=0;i--)
        if((1<<i)&level)
            x=f[x][i];
    return x;
}

inline int LCA(int u,int v)
{
    if(deep[u]<deep[v])
        swap(u,v);
    u=skip(u,deep[u]-deep[v]);
    if(u==v)
        return u;
    for(int i=20;i>=0;i--)
        if(f[u][i]!=f[v][i])
            u=f[u][i],v=f[v][i];
    return f[u][0];
}

int main()
{
    while(~scanf("%d",&n))
    {
        ++n;
        int u,v;
        cnt=tot=0;
        memset(head,0,sizeof(head));
        memset(tree,0,sizeof(tree));
        memset(deep,0,sizeof(deep));
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            ++u,++v;
            addedge(u,v);
        }
        dfs(1,0);
        scanf("%d",&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&query[i].u,&query[i].v);
            ++query[i].u,++query[i].v;
            query[i].grand=LCA(query[i].u,query[i].v);
            query[i].deep=deep[query[i].grand];
        }
        sort(query,query+m);
        int ans=0,tmp1,tmp2;
        for(int i=0;i<m;i++)
        {
            tmp1=sum(in[query[i].u]);
            tmp2=sum(in[query[i].v]);
            if(tmp1||tmp2)
                continue;
            ++ans;
            add(in[query[i].grand],1);
            add(out[query[i].grand]+1,-1);
        }
        printf("%d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值