LOJ #10131. 「一本通 4.4 例 2」暗的连锁

在这里插入图片描述

analysis

这是一道好题诶!对于我这种蒟蒻来说是属于比较考思维的一道题
开始看到题一脸懵逼,根本不知道从何下手
看到书上的讲解后才恍然大悟:原来就是个差分嘛!

我们枚举每条非树边(x,y),把x到y路径上所有边的边权+1,对于每条树边,如果边权为0,则切断它之后原图已经不联通,第二条边随便切一条,共m种方案。如果边权为1,则第二步必须切断对应的那条边,方案数1,如果边权大于2则没有方案。

但是怎么维护呢?差分!怎么差分呢?LCA!

这就是这个题的目的,将LCA问题放在差分中来考

若是有人在我思考这个题的时候突然告诉我说:这个题用差分!十有八九我都能够想得出来

不过既然没有人告诉我,我就自己告诉自己

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define ll long long

template<typename T>void read(T &x){
    x=0;char r=getchar();T neg=1;
    while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
    while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
    x*=neg;
}

int n,m,cnt=0;
const int maxn=1e5+10,maxm=2e5+10;
struct node{int e;int nxt;}edge[maxn<<2];
int head[maxn];

inline void addl(int u,int v){
    edge[cnt].e=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt++;
}

int siz[maxn];
int son[maxn];
int dep[maxn];
int fa[maxn];

void dfs1(int u,int f){
    dep[u]=dep[f]+1;
    fa[u]=f;
    siz[u]=1;
    son[u]=0;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].e;
        if(v==f)
            continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        son[u]=((siz[son[u]]<siz[v])?v:son[u]);
    }
}

int top[maxn];

void dfs2(int u){
    if(son[u]){
        top[son[u]]=top[u];
        dfs2(son[u]);
    }
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].e;
        if(!top[v]){
            top[v]=v;
            dfs2(v);
        }
    }
}

inline int LCA(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]){
            swap(u,v);
        }
        u=fa[top[u]];
    }
    return ((dep[u]<dep[v])?u:v);
}

int W[maxn];

void updateW(int pos,int f){
    for(int i=head[pos];i!=-1;i=edge[i].nxt){
        int v=edge[i].e;
        if(v==f)
            continue;
        updateW(v,pos);
        W[pos]+=W[v];
    }
}

void debug(){
    loop(i,1,n){
        printf("%d\n",W[i]);
    }
}

int main(){
    #ifndef ONLINE_JUDGE
    freopen("datain.txt","r",stdin);
    #endif // ONLINE_JUDGE
    
    read(n),read(m);
    clean(head,-1);
    clean(W,0);
    loop(i,1,n-1){
        int ai=0,bi=0;
        read(ai),read(bi);
        addl(ai,bi),addl(bi,ai);
    }

    dfs1(1,0);
    top[1]=1;
    dfs2(1);

    loop(i,1,m){
        int ai=0,bi=0;
        read(ai),read(bi);
        ++W[ai];
        ++W[bi];
        W[LCA(ai,bi)]-=2;
    }
    updateW(1,0);

    int res=0;
    loop(i,1,n){
        if(W[i]==1){
            ++res;
        }
        else if(W[i]==0&&i!=1){
            res+=m;
        }
    }
    printf("%d\n",res);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值