3592: 【USACO】锻炼路线

3592: 【USACO】锻炼路线

时间限制: 1.000 Sec  内存限制: 256 MB
提交: 6  解决: 2
[命题人:][下载数据: 90]

提交状态报告

题目描述

奶牛Bessie意识到为了保持好的体形她需要更多地进行锻炼。她需要你帮助她选择在农场里每天用来晨跑的路线。
农场由N块草地组成(1≤N≤2⋅105),方便起见编号为1…N,由M条双向的小路连接(1≤M≤2⋅105)。作为一种遵循规律的生物,奶牛们倾向于使用其中特定的N−1条小路作为她们日常在草地之间移动的路线——她们管这些叫“常规的”小路。从每块草地出发都可以仅通过常规的小路到达所有其他草地。

为了使她的晨跑更加有趣,Bessie觉得她应该选择一条包含一些非常规的小路的路线。然而,使用常规的小路能够使她感到舒适,所以她不是很想在她的路线中使用过多非常规的小路。经过一些思考,她认为一条好的路线应当形成一个简单环(能够不经过任何草地超过一次的情况下回到起点),其中包含恰好两条非常规的小路。

请帮助Bessie计算她可以使用的好的路线的数量。两条路线被认为是相同的,如果它们包含的小路的集合相等。

输入

输入的第一行包含N和M。以下M行每行包含两个整数ai和bi,描述了一条小路的两端。其中前N−1条是常规的小路。

输出

输出Bessie可以选择的路线的总数。

样例

输入  复制

5 8 1 2 1 3 1 4 1 5 2 3 3 4 4 5 5 2

输出  复制

4

来源/分类

USACO 2019 January Platinum 

题解

#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#define maxn 400005
#define ll long long
using namespace std;
int pre[maxn],to[maxn],las[maxn],mk[maxn],sum[maxn],inc;
int fa[maxn][22],dep[maxn],st[maxn],en[maxn],lca[maxn],n,m;
ll ans=0;
void ins(int a,int b){pre[++inc]=las[a],las[a]=inc,to[inc]=b;}
void GetFa(int x,int f)
{
    dep[x]=dep[f]+1,fa[x][0]=f;
    for(int i=1;i<=21;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=las[x],y=to[i];i;i=pre[i],y=to[i])
        if(y!=f) GetFa(y,x);
}
void dfs(int x,int f,int now)
{
    sum[x]=(now+=mk[x]);
    for(int i=las[x],y=to[i];i;i=pre[i],y=to[i])
        if(y!=f) dfs(y,x,now);
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    int delta=dep[x]-dep[y];
    for(int i=21;i>=0;i--)
        if((delta>>i)&1) x=fa[x][i];
    if(x==y) return x;
    for(int i=21;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int Close(int x,int anc)
{
    if(x==anc) return -1;
    for(int i=21;i>=0;i--)
        if(fa[x][i]&&dep[fa[x][i]]>dep[anc]) x=fa[x][i];
    return x;
}
map<pair<int,int>,int> mp;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<n;i++)
        scanf("%d%d",&u,&v),ins(v,u),ins(u,v);
    GetFa(1,0);
    for(int i=n;i<=m;i++)
    {
        scanf("%d%d",&st[i],&en[i]);
        lca[i]=LCA(st[i],en[i]);
        int ux=Close(st[i],lca[i]);
        int uy=Close(en[i],lca[i]);
        if(ux!=-1) ans-=(++mk[ux]);
        if(uy!=-1) ans-=(++mk[uy]);
        if(ux!=-1&&uy!=-1)
        {
            if(ux>uy) swap(ux,uy);
            ans-=(mp[make_pair(ux,uy)]++);
        }
    }
    dfs(1,0,0);
    for(int i=n;i<=m;i++)
        ans+=sum[st[i]]+sum[en[i]]-2*sum[lca[i]];
    printf("%lld\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值