zoukankan      html  css  js  c++  java
  • 【agc006f】Blackout(神仙题)

    【agc006f】Blackout(神仙题)

    翻译

    给定一个(n*n)的网格图,有些格子是黑色的。如果((x,y),(y,z))都是黑色的,那么((y,x))也会被染黑,求最终黑格子数量。

    题解

    网格图我们显然是存不下的,把它转化成图来考虑。于是题目变成了:给定一个(n)个点(m)条边的图,如果(x ightarrow y)(y ightarrow z)的边都存在,那么连边(z ightarrow x),回答边的数量。

    然后开始手动翻译题解。

    首先,我们可以计算每一个弱联通块(把边看成无向边的联通块),那么答案显然就是所有弱联通块的答案的总和。我们先假定图是一个弱联通图。

    考虑这样一种情况,我们把点依次标号,然后在(i)(i+1)之间连边,那么如果(s)(t)之间存在边(s ightarrow t),那么当且仅当(tequiv s+1(mod 3))。具有一定启发意义,我们考虑在模(3)的意义下搞点事情。我们用(A,B,C)给所有点做标记,并且强制要求对于任意一条边,只可能是(A ightarrow B)(B ightarrow C)(C ightarrow A)。这样标号的方式可能不存在,但是不难证明一旦存在合法的标号方案,那么标号的方法唯一(不考虑循环(ABC)的顺序)。你可以把整个图给(dfs)一遍,这样子可以得到唯一的染色方案,或者证明它不存在。

    通过标号的结果,我们可以得到三种情况,给出每种情况下的结论,等下再给出证明。

    • 当标号存在,但是并没有用到所有的三种颜色,那么你无法在这个联通块中进行任何操作。
    • 当标号存在,并且所有的三种颜色都被用到,那么你可以把所有(AB)之间连边,(BC)之间连边,(CA)之间连边,并且只能连这些边。
    • 当标号方案不存在,你可以给任意一对点之间连边,包括自环。

    利用结论,可以很容易的计算出答案,时间复杂度(O(m))。代码如下,证明内容(当然是翻译的啊)在代码后面。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define MAX 100100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    struct Line{int v,next,w;}e[MAX<<1];
    int h[MAX],cnt=1,dg[MAX];
    inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
    int n,m;ll ans;
    int vis[MAX],f[3],edge,size;bool label;
    void dfs(int u,int d)
    {
    	vis[u]=d;f[d]+=1;++size;
    	for(int i=h[u];i;i=e[i].next)
    	{
    		int v=e[i].v;if(e[i].w==1)++edge;
    		if(vis[v]==-1)dfs(v,(d+e[i].w)%3);
    		else if(vis[v]!=(vis[u]+e[i].w)%3)label=false;
    	}
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=m;++i)
    	{
    		int x=read(),y=read();
    		Add(x,y,1);Add(y,x,2);
    	}
    	memset(vis,-1,sizeof(vis));
    	for(int i=1;i<=n;++i)
    		if(vis[i]==-1)
    		{
    			label=true;f[0]=f[1]=f[2]=0;size=edge=0;dfs(i,0);
    			if(label)ans+=(!min(f[0],min(f[1],f[2])))?(edge):(1ll*f[0]*f[1]+1ll*f[1]*f[2]+1ll*f[2]*f[0]);
    			else ans+=1ll*size*size;
    		}
    	cout<<ans<<endl;
    	return 0;
    }
    
    • 当标号存在,但是并没有用到所有的三种颜色,那么你无法在这个联通块中进行任何操作。

      如果存在边((x,y))((y,z)),那么必定意为这所有的三种颜色都会被用到。既然如此,那么意味着这里不存在上述的边,所以你不能连出任何一条新边。

    • 当标号存在,并且所有的三种颜色都被用到,那么你可以把所有(AB)之间连边,(BC)之间连边,(CA)之间连边,并且只能连这些边。

      必定存在若干形如((x,y),(y,z))这样的边,我们不妨令(x)(A)(y)(B)(z)(C)。我们可以看出所有新连的边加上原边会构成一个个三角形。举个例子,令(v)存在一条边((v,x)),那么必定存在边((y,v)),那么我们不难证明任意一个(v)一定和(x,y,z)三个点中的两个有直接的边相连。所以任意的(A)都会连出一条(A ightarrow B),其他的边同理。

    • 当标号方案不存在,你可以给任意一对点之间连边,包括自环。

      我们证明至少会存在一个自环。既然标号方案不存在,那么必定存在一个环导致了矛盾,注意,这个环不一定是有向环。那么这个环至少存在两条边((x,y)),((y,z)),那么我们可以连上((z,x)),那么等价于我们看这个环的时候可以直接跳过(y)。既然原先的环会导出矛盾,那么当前这个环照样会导出矛盾,那么我们重复这个过程,就可以得到自环。而其他的边存在的原因和前面两个证明类似,不再重复证明。

  • 相关阅读:
    js代码与html代码分离示例
    day24_Nginx学习笔记
    bookStore商城开发文档
    API Management Architecture Notes
    Taking A Fresh Look At What Open Source API Management Architecture Is Available
    使用API Gateway
    Qcon2017实录|Service Mesh:下一代微服务
    聊聊 API Gateway 和 Netflix Zuul
    项目长期运维中产生的一些问题
    忆情天书的由来
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9683682.html
Copyright © 2011-2022 走看看