2017 ECNA Problm F: Keeping On Track (图,DFS)

Acmar and Ibmar are at war! You are in charge of a rail network that transports important supplies throughout the great state of Acmar during this delicate time. The rail system is made up of a set of rail lines which meet at various junction points. While there is no limit to the number of rail lines that can meet at a junction, the network is set up so that there is only one path between any two junctions. You’ve tried to argue for some redundancy in the system, i.e., extra rail lines so that there are two or more paths connecting some junctions, but it’s wartime and budgets are tight.
However, this may soon change as you’ve just been given some terrible news from double agents working in Ibmar: within the next month enemy spies plan to blow up one of the junctions! Unfortunately, the exact junction is not known, but knowing your enemy well you are certain that they will undoubtedly strike the critical junction, specifically the junction whose removal disconnects the most pairs of other remaining junctions in the system. You don’t have much time to act, so the most you can do is add one new line connecting two currently unconnected junctions, thereby reducing the number of disconnected pairs after the critical junction has been destroyed. Your job is to determine how to make the number of disconnected pairs as small as possible by adding in the best possible rail line.

Input

Input starts with a line containing an integer n (2 ≤ n ≤ 10 000) indicating the number of rail lines in the
system. Following that are n lines of the form i1 i2 indicating that a rail line connects junctions i1 and i2.
Junctions are numbered consecutively starting at 0. All rail lines are two-way and no rail line appears more than once in the input. There is exactly one path between any two junction points given in the input.

Output

Display two values n1 and n2, where n1 is the number of pairs of junctions which will be disconnected when the enemy destroys the critical junction, and n2 is the number of pairs of junctions still disconnected after you add in the best possible rail line. There will never be more than one critical junction.

Sample Input 1

6
0 1
1 2
2 3
2 4
4 5
4 6

Sample Output 1

11 5



题意:

给出一棵有n(n≤10 000)条边的树,存在一个critical结点,使得删除该critical结点后,不连通的结点对最多。
①求出删除该critical结点后不连通的结点对的数量。
②在删除critical结点后加一条最优的边,使得恢复连通的结点对最多,同时求出加上该最优边后依旧不连通的结点对的数量。



分析:

首先想到,枚举每个结点进行DFS

由于该图是一个n条边的树,则一定有n+1个结点;
对于任意一个结点x,以其为根,设其子结点数量为s1、s2、s3…
那么删除x后的不连通结点对数量:
a n s = ∑ s i ∗ ( n − s i ) 2 ans = \frac{\sum s_{i}*(n-s_{i})}{2} ans=2si(nsi)
可以发现 si * (n - si) 全部加起来,每对结点对都会相互多乘一次,所以最后结果应当除以2

但是这样时间复杂度会达到O(N^2)

所以关键在于如何只DFS一次就得出每个结点的ans,降低时间复杂度为O(N);

那么我们可以从任意结点开始DFS;

对于结点x,向下DFS(即向子树方向)得到的子结点数量为s1、s2、s3…, 这个依旧很好求得,而因为 结点x不一定为根结点,其子结点数量还应包括向上DFS(即向父结点方向)得到的s0
则易知:
s 0 = n − ∑ s i s_{0}=n-\sum s_{i} s0=nsi


而对于第二个问题,只需要以critical结点为根进行DFS,求得其最大的两个子结点数量max1、max2,那么第二个问题的答案即使max_ans - max1*max2。



以下代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=10000+7;
int n;                   //注意因为是双向边,所以数组大小应为maxn*2
int head[maxn*2],Next[maxn*2],to[maxn*2],cnt=0;
int critical,max_ans=0,max1=0,max2=0;
bool vis[maxn];
void addedge(int u,int v)
{
    cnt++;
    to[cnt]=v;
    Next[cnt]=head[u];
    head[u]=cnt;
}
int dfs1(int x)
{
    vis[x]=true;
    int ans=0,son=0;
    for(int i=head[x];i!=-1;i=Next[i])
    {
        if(!vis[to[i]])
        {
            int t=dfs1(to[i]);    //向下DFS统计的子结点
            ans+=t*(n-t);
            son+=t;
        }
    }
    ans+=son*(n-son);     //上一层的子结点
    ans/=2;
    if(ans>max_ans)
    {
        critical=x;
        max_ans=ans;
    }
    return son+1;         //记得+1,即结点x自身
}
int dfs2(int x)
{
    vis[x]=true;
    int son=0;
    for(int i=head[x];i!=-1;i=Next[i])
    {
        if(!vis[to[i]])
            son+=dfs2(to[i]);
    }
    return son+1;
}
int main()
{
    memset(head,-1,sizeof(head));
    memset(Next,-1,sizeof(Next));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int u,v;
        scanf("%d %d",&u,&v);    //建立双向边
        addedge(u,v);
        addedge(v,u);
    }
    memset(vis,false,sizeof(vis));
    dfs1(0);
    memset(vis,false,sizeof(vis));
    vis[critical]=true;
    for(int i=head[critical];i!=-1;i=Next[i])
    {
        if(!vis[to[i]])
        {
            int t=dfs2(to[i]);
            if(t>max1)
            {
                max2=max1;   //不要漏了这句
                max1=t;
            }
            else if(t>max2)
                max2=t;
        }
    }
    printf("%d %d\n",max_ans,max_ans-max1*max2);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值