zoukankan      html  css  js  c++  java
  • BZOJ_4238_电压_树上差分+dfs树

    BZOJ_4238_电压_树上差分+dfs树

    Description

    你知道Just Odd Inventions社吗?这个公司的业务是“只不过是奇妙的发明(Just Odd Inventions)”。这里简称为JOI社。
    JOI社的某个实验室中有着复杂的电路。电路由n个节点和m根细长的电阻组成。节点被标号为1~N
    每个节点有一个可设定的状态【高电压】或者【低电压】。每个电阻连接两个节点,只有一端是高电压,另一端是低电压的电阻才会有电流流过。两端都是高电压或者低电压的电阻不会有电流流过。
    某天,JOI社为了维护电路,选择了一根电阻,为了能让【只有这根电阻上的电流停止流动,其他M-1根电阻中都有电流流过】,需要调节各节点的电压。为了满足这个条件,能选择的电阻共有多少根?
    对了,JOI社这个奇妙的电路是用在什么样的发明上的呢?这是公司内的最高机密,除了社长以外谁都不知道哦~
    现在给出电路的信息,请你输出电路维护时可以选择使其不流的电阻的个数。

    Input

    第一行两个空格分隔的正整数N和M,表示电路中有N个节点和M根电阻。
    接下来M行,第i行有两个空格分隔的正整数Ai和Bi(1<=Ai<=N,1<=Bi<=N,Ai≠Bi),表示第i个电阻连接节点Ai和节点Bi。

    Output

    输出一行一个整数,代表电路维护时可选择的使其不流的电阻个数。

    Sample Input

    4 4
    1 2
    2 3
    3 2
    4 3

    Sample Output

    2

    HINT

    可以选择第一根电阻或第四根电阻。
     
    2<=N<=10^5
    1<=M<=2*10^5
    不保证图是连通的,不保证没有重边

    首先把图中的边分成树边和非树边。把非树边分成偶环和奇环。
    首先有最基本的性质1:偶环上的边不能拆,并且拆完之后不能有奇环。
    性质2:如果图里只有一个奇环。可以选择一个非树边,否则不可以选择非树边,因为此时要么非树边在偶环里,要么拆掉这条非树边图中仍存在奇环。
    性质3:选择的树边需要满足:被所有的奇环覆盖并且不被任何一个偶环覆盖。
    于是树上差分即可。
     
    代码:
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    #define N 200050
    int head[N],to[N<<1],nxt[N<<1],vis[N],cnt=1,n,m;
    int odd[N],even[N],fa[N],dep[N];
    inline void add(int u,int v) {
        to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
    }
    void dfs(int x,int y,int idx) {
        fa[x]=y; dep[x]=dep[y]+1; vis[x]=1;
        int i;
        for(i=head[x];i;i=nxt[i]) {
            if((i^1)==idx) continue;
            if(!vis[to[i]]) {
                dfs(to[i],x,i);
                even[x]+=even[to[i]];
                odd[x]+=odd[to[i]];
            }else {
                if(dep[to[i]]>dep[x]) continue;
                int d=dep[x]-dep[to[i]];
                if(d&1) {
                    even[x]++; even[to[i]]--; even[0]++;
                }else {
                    odd[x]++; odd[to[i]]--; odd[0]++;
                }
            }
        }
    }
    int main() {
        scanf("%d%d",&n,&m);
        int i,x,y;
        for(i=1;i<=m;i++) {
            scanf("%d%d",&x,&y);
            add(x,y); add(y,x);
        }
        for(i=1;i<=n;i++) {
            if(!vis[i]) {
                dfs(i,0,0);
            }
        }
        int ans=0;
        for(i=1;i<=n;i++) {
            if(fa[i]&&odd[i]==odd[0]&&!even[i]) ans++;
        }
        if(odd[0]==1) ans++;
        printf("%d
    ",ans);
    }
    
  • 相关阅读:
    列表、元组、字典等相关命令
    字符串相关命令
    Python简介
    二进制的趣事
    Shell脚本基础
    Linux基本服务
    一次性计划任务at与周期性计划任务crontab
    Linux权限管理
    python-文件操作
    python-初识python
  • 原文地址:https://www.cnblogs.com/suika/p/9023008.html
Copyright © 2011-2022 走看看