zoukankan      html  css  js  c++  java
  • AT1226/「JOISC 2014 Day3」电压 题解

    题意

    \(~~~~\) 给定了一张图,求该图有多少条边满足删掉它后图变为二分图,且删去的边的两点在二分图中要在同一部中。

    题解

    \(~~~~\) 二分图的判定条件是图中没有 奇环,因此合法边需要满足删去它后图上没有奇环。

    \(~~~~\) 那么我们求出所有奇环的边的交即可,然后还要使得这些边不能在偶环上,因为若一个奇环与偶环有交那么它们即使删去相交的边也还是有一个更大的奇环;或者原图没有奇环,即原图就是二分图,那么删去这条边后二分图的染色方案仍和原来一样,此时删去的边的两端分属两部,这也是不合法的,这里建议自己画图看一下。

    \(~~~~\) 那我们如何求出每条边在多少奇环和偶环上?

    \(~~~~\) 考虑建出原图的DFS树,然后考虑每条返祖边的贡献

    • 若环上只有一条返祖边:

      • 若该环为奇环,这个环上所有树边都是在奇环中的。(由于有多个返祖边)
      • 若该环为偶环,那么同上。
    • 若环上有2+返祖边,显然两条返祖边有两个小环,且两条返祖边的点经过的树边一定有交,否则就会有一条树边被经过两次,这样就没有简单环了。而两条返祖边形成的大环可以再与其他返祖边组成环,因此我们证两条返祖边的情况然后进行归纳即可。

      • 若两个环分别为奇环和偶环,那么它们形成了一个大奇环,此时大奇环上的树边全部少统计了一遍,但我们也没有数这个奇环,所以不统计小奇环上的树边没有影响,而偶环上的即使统计过显然也不会成为答案,因此对答案没有影响。
      • 若两个都是奇环,那么它们的交是不在形成的大偶环上的,而其他在大偶环上的边至少不在一个奇环上,本身就不会被统计入答案里面,因此对答案没有影响。
      • 若两个都是偶环,那么显然本身都不会被计入答案,自然形成大偶环也对答案没有影响。

      综上,做题当时少考虑这种情况但对答案没有影响。.

    \(~~~~\) 然后只有一条返祖边的情况边差分做即可,一遍DFS即可。

    \(~~~~\) 注意只有一个奇环时那条返祖边也是合法的。

    \(~~~~\) 时间复杂度 \(\mathcal{O(n)}\)

    代码

    查看代码
    #include <map>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #define mp(a,b) make_pair(a,b)
    using namespace std;
    bool vis[100005];
    int fa[1000005],cnt[100005][2],dep[100005],jh;
    vector < pair<int,int> >G[100005];
    void dfs(int u,int d)
    {
    	dep[u]=d;
    	vis[u]=true;
    	for(int i=0;i<G[u].size();i++)
    	{
    		int v=G[u][i].first,w=G[u][i].second;
    		if(w==fa[u]) continue;
    		if(vis[v])
    		{
    			if(dep[v]>dep[u]) continue;
    			int k=(dep[u]-dep[v])&1;
    			if(k==0) jh++; 
    			cnt[u][k]++;
    			cnt[v][k]--;
    		}
    		else
    		{
    			fa[v]=w;
    			dfs(v,d+1);
    			cnt[u][0]+=cnt[v][0];cnt[u][1]+=cnt[v][1];
    		}
    	}
    } 
    int main() {
    	int n,m;
    	scanf("%d %d",&n,&m);
    	for(int i=1,u,v;i<=m;i++)
    	{
    		scanf("%d %d",&u,&v);
    		G[u].push_back(mp(v,i));
    		G[v].push_back(mp(u,i));
    	}
    	for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0);
    	int Ans=0;
    //	for(int i=1;i<=n;i++) printf("odd=%d even=%d\n",cnt[i][0],cnt[i][1]);
    	for(int i=1;i<=n;i++)
    	{
    		if(fa[i]&&cnt[i][0]==jh&&!cnt[i][1]) Ans++;
    	}
    	printf("%d\n",Ans+(jh==1));//AT上记得换行
    	return 0;
    }
    
  • 相关阅读:
    win8 需要管理员权限才能删除此应用程序
    windows 8 解决端口(COM和LPT)问题:Prolific USBtoSerial Comm Port(COM4)驱动异常的问题
    sqlite 获取数据库中的所有表
    ListView simpleAdapter的基本使用
    一根长度为133米的材料需要截成长度为19米和23米的短料,求两种短料各截多少根时剩余的材料最少?
    打印输出九九乘法表
    写一个函数,输入一个十六进制,输出相应的十进制数。
    求3100的素数!
    从键盘输入若干个同学的成绩,统计并输出最高成绩 最低成绩 平均分,当输入负数时结束输入
    编写两个函数,分别求由键盘输入两个整数的最大公约数和最小公倍数。用主函数调用这两个函数,并输出结果
  • 原文地址:https://www.cnblogs.com/Azazel/p/14578167.html
Copyright © 2011-2022 走看看