zoukankan      html  css  js  c++  java
  • 【agc006f】Blackout

    Description

    给你一个n行n列的网格,第i行第j列的格子用(i,j)表示。

    一开始的时候有m个格子被涂成黑色,其他的格子都是白色,具体一点,涂成黑色的格子为(a1,b1),(a2,b2),(a3,b3),…,(am,bm)。

    你的目标是按照以下规则将尽可能多的白色格子涂成黑色:如果存在三个格子(x,y),(y,z),(z,x)满足(x,y)和(y,z)都是黑格子并且(z,x)是白格子(其中x,y,z都是[1,n]之间的整数),那么你可以将(z,x)涂成黑色。

    输出你不能继续操作时,黑格子的最大数量。


    Solution

    我们可以将其转化为有向图,若存在(x,y)和(y,z)这两条边,则我们可以添加第三条边(z,x),求边的最大数量。

    首先,各弱联通块之间互不影响,因此我们可以将各弱联通块分开考虑。

    我们可以得到:

    (1)一条长度为2的链可形成一个三元环。(x,y)(y,z)→(x,y)(y,z)(z,x) 

    (2)一个二元环会形成自环。(x,y)(y,x)→(x,y)(y,x)(x,x)(y,y)

    (3)所有连向自环点的点都会形成自环。(x,y)(y,y)→(x,y)(y,y)(y,x)(x,x)

    (4)由(2)和(3)得若一个弱联通块存在二元环或自环,则该弱联通块可拓展为完全图。

    我们考虑各弱联通块是否存在二元环或自环,用三种颜色进行染色(如三元环中三个点为不同的颜色),进行分类讨论:

    (1)只出现了一种或两种颜色:说明该弱联通块无法添加新边;

    (2)同一个点被染上两种或三种颜色:出现二元环或自环,该弱联通块可拓展为完全图;

    (3)default:染色成功,染色为0的点连向染色为1的点,染色为1的点连向染色为2的点,染色为2的点连向染色为0的点。

    Code

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 #define next _next
     6 typedef long long ll;
     7 struct edge{
     8     int to,next,data;
     9 }e[200010];
    10 int n,m,head[100010];
    11 int tot,size,maxn,a[3],color[100010]={0};
    12 ll ans=0;
    13 bool flag,vis[100010]={false},visp[100010]={false};
    14 void dfs(int u){
    15     vis[u]=true;
    16     size++;
    17     a[color[u]]++;
    18     maxn=max(maxn,color[u]);
    19     for(int i=head[u];~i;i=e[i].next)
    20         if(!visp[(i+1)/2]){
    21             visp[(i+1)/2]=true;
    22             tot++;
    23             int v=e[i].to;
    24             if(!vis[v]){
    25                 color[v]=(color[u]+e[i].data+3)%3;
    26                 dfs(v);
    27             }
    28             else
    29                 flag|=(color[v]!=(color[u]+e[i].data+3)%3);
    30         }
    31     return;
    32 }
    33 int main(){
    34     memset(head,-1,sizeof(head));
    35     scanf("%d%d",&n,&m);
    36     for(int i=1,cnt=0;i<=m;i++){
    37         int u,v;
    38         scanf("%d%d",&u,&v);
    39         e[++cnt]=(edge){v,head[u],1};
    40         head[u]=cnt;
    41         e[++cnt]=(edge){u,head[v],-1};
    42         head[v]=cnt;
    43     }
    44     for(int i=1;i<=n;i++)
    45         if(!vis[i]){
    46             tot=size=maxn=a[0]=a[1]=a[2]=0;
    47             flag=false;
    48             dfs(i);
    49             if(flag)
    50                 ans+=(ll)size*size;
    51             else if(!a[0]||!a[1]||!a[2])
    52                 ans+=tot;
    53             else
    54                 ans+=(ll)a[0]*a[1]+(ll)a[1]*a[2]+(ll)a[2]*a[0];
    55         }
    56     printf("%lld\n",ans);
    57     return 0;
    58 }
  • 相关阅读:
    Firefly 介绍
    9秒社团跨平台开发引擎CrossApp宣布正式开源
    页面置换算法
    Selenium
    C++虚函数、虚继承
    链表题目
    二叉树分类
    求连续子数组的最大和
    拓扑排序
    二叉树题目
  • 原文地址:https://www.cnblogs.com/gzez181027/p/agc006f.html
Copyright © 2011-2022 走看看