zoukankan      html  css  js  c++  java
  • [BZOJ4238]电压

    题意:

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

    输入:

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

    输出:

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

    Input:

    4 4
    1 2
    2 3
    3 2
    4 3

    Output:

    2

    思路:

    首先,题目的意思不是去掉一个电阻(应该只有我zz地这么想了),按照题目的意思,如果选择了这个电阻,那么它的两端的应该是同样的电压状态。

    我们可以将高电压认为是1,低电压认为是0,为了让这幅图满足条件,也就是相邻的异色,可以理解为是二分图。

    那么我们考虑什么情况下会不是异色?

    如果一些电阻连成了一个环,而环上恰好有奇数条边的时候,一定会出现矛盾(手完一下就可以确定)。所以我们要选择的边一定是出现在奇环上的,并且要出现在所有的奇环上(否则其他地方仍然会有不满足的)

    但是奇环上的也不一定是满足条件的,如果这条边还恰好出现在一个及以上的偶数环上,要满足偶数环的条件是这两个一定是异色的,与要求的同色不符。

    所以实际上我们要找的边是出现在所有的奇数环上且不出现在偶数环上。

    那么如何更新环上的边?

    这里可以考虑树上差分,记录一个cnt数组,先按dfs序判断这条边是否为返祖边,如果是,那么在当前的位置cnt++,在终点位置cnt--。对于x->y的一条边来说,只要考虑cnt[y]的值,就是这条边的贡献(这里的边指的是树边),至于返祖边可以直接在找到的时候计算。

    ps:这里是奇偶分开算了,其实可以直接用一个数组,奇加偶减。然后或许会问如果不存在环怎么办,那么所有的边的贡献都是0,还是和奇环的总数0相同,所以是莫得影响的

    #include<bits/stdc++.h>
    #define M 200005
    using namespace std;
    void Rd(int &res) {
    	res=0;
    	char c;
    	while(c=getchar(),c<48);
    	do res=(res<<1)+(res<<3)+(c-'0');
    	while(c=getchar(),c>=48);
    }
    int la[M],pr[M<<1],to[M<<1],tot,n,m,dfn[M],dep[M],id,Odd[M],Even[M],In[2][M],cnt;//这条边在奇环上 这条边在偶数环上
    void add(int x,int y) {
    	to[++tot]=y,pr[tot]=la[x],la[x]=tot;
    }
    struct node {
    	int x,y;
    } a[M];
    bool mark[M<<1];
    void dfs(int x) {
    	dfn[x]=++id;
    	for(int i=la[x]; i; i=pr[i]) {
    		int y=to[i];
    		if(!dfn[y]) {
    			dep[y]=dep[x]+1,mark[i]=1;
    			if(i&1)mark[i+1]=1;
    			else mark[i-1]=1;
    			dfs(y);
    			Even[(i+1)/2]+=In[0][y],In[0][x]+=In[0][y];
    			Odd[(i+1)/2]+=In[1][y],In[1][x]+=In[1][y];
    		} else if(mark[i]||dfn[y]>dfn[x])continue;
    		else if((dep[x]-dep[y])&1)In[0][x]++,In[0][y]--,Even[(i+1)/2]++;
    		else In[1][x]++,In[1][y]--,Odd[(i+1)/2]++,cnt++;
    	}
    }
    int ans;
    int main() {
    	Rd(n),Rd(m);
    	for(int x,y,i=1; i<=m; i++)Rd(x),Rd(y),add(x,y),add(y,x),a[i]=(node)<%x,y%>;
    	for(int i=1; i<=n; i++) {
    		if(dfn[i])continue;
    		dfs(i);
    	}
    	for(int i=1; i<=m; i++) {
    		if(Even[i])continue;
    		if(Odd[i]==cnt)ans++;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    AMF序列化技巧
    为什么用ByteArray序列化的对象如此小?
    解决Asp.net中翻页问题的自定义用户控件
    新建对象:反射会调用构造函数,clone不会调用构造函数
    Java 的传值小例子
    JDK中设计模式
    tryfinally中的返回值
    c++类中的常量(注意)
    创建有个性的对话框之MFC篇(转)
    用VC在IE浏览器的工具条上添加命令按钮(转 可以借鉴)
  • 原文地址:https://www.cnblogs.com/cly1231/p/11594445.html
Copyright © 2011-2022 走看看