[agc006_f] Blackout

Description

​ 给你一个\(N *N\)的网格,初始\(M\)个格子\((a_i,b_i)\)被涂成黑色,其余为白色。每次可以将一些格子涂成黑色,规则如下如果存在三个格子\((x,y),(y,z),(z,x)\)满足\((x,y),(y,z)\)均为黑色,且\((z,x)\)为白色,那么我们可以把\((z,x)\)涂成黑色。输出不能继续操作时,黑格子的最大数量。

Hint

\(1<=N,M<=10^5,1<=a_i,b_i<=N\),并且所有的\((a_i,b_i)\)互不相同。

Solution

​ 先转化模型,将网格变成邻接矩阵,于是题目转化为:有一个有向图,如果x->y,y->z,加上z->x。重复该过程直到不能再添加。求最终图中有多少边。

​ 首先,各个弱联通块之间互不影响,所以,每个弱联通块单独考虑。手玩一下发现

1、一条长度为2的链会构成一个三元环。

2、如果出现二元环,一定会出现自环

3、所有连向有自环的点的点,会出现自环

4、一旦出现二元环或者自环原弱联通块会变成完全图

​ 那么,我们考虑如何判断该联通块会不会出现二元环,或者自环。那么我们将弱联通块三染色,分类讨论:

1、染色失败,如果每种颜色都出现过,说明出现了二元环或者自环,原图最终一定是一个完全图;如果,只出现了一种或者两种颜色,说明无法增加新的边。

2、染色成功,则弱联通块的答案为,染色为0的点连向染色为1的点,染色为1的点连向染色为2的点,染色为2的点连向染色为0的点。

​ 分类统计答案即可。

Code

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 10;
 
int n, m, ecnt, h[maxn], col[maxn];
ll cnt[5];
struct enode{
  int v, n, w;
  enode() {}
  enode(int _v, int _n, int _w):v(_v), n(_n), w(_w) {}
}e[maxn << 1];
 
inline void addedge(int u, int v, int w) {
  ecnt ++; e[ecnt] = enode(v,h[u],w); h[u] = ecnt;
}
 
int flag = 0;
void dfs(int u) {
  cnt[col[u]] ++;
  for(int i = h[u];~ i;i = e[i].n) {
    if(e[i].w == 1) cnt[3] ++;
    int v = e[i].v, tmp = (col[u] + e[i].w) % 3;
    if(col[v] == -1) {
      col[v] = tmp;
      dfs(v);
    }
    else if(col[v] != tmp) flag = 1;
  }
}
 
int main() {
  scanf("%d%d", &n, &m); ecnt = 0;
  memset(h,-1,sizeof(h));
  for(int i = 1;i <= m;i ++) {
    int u, v;
    scanf("%d%d", &u, &v);
    addedge(u,v,1);
    addedge(v,u,2);
  }
  ll ans = 0;
  memset(col,-1,sizeof(col));
  for(int i = 1;i <= n;i ++) {
    if(~ col[i]) continue;
    cnt[0] = cnt[1] = cnt[2] = cnt[3] = 0;
    col[i] = 0;
    flag = 0;
    dfs(i);
    if(flag) {
      ll tmp = cnt[0] + cnt[1] + cnt[2];
      ans = ans + 1LL * tmp * tmp;
      continue;
    }
    if(cnt[0] && cnt[1] && cnt[2]) {
      ans = ans + cnt[0] * cnt[1] + cnt[1] * cnt[2] + cnt[2] * cnt[0];
      continue;
    }
    ans = ans + cnt[3];
  }
  printf("%lld\n", ans);
  return 0;
}

转载于:https://www.cnblogs.com/ezhjw/p/9520115.html

可以使用Python编写一个函数来实现这个计算过程。具体实现如下: ```python import datetime def count_no_work_days(start_date, end_date): """ 计算某个时间段内不用上班的天数 :param start_date: 开始日期,格式为YYYY-MM-DD :param end_date: 结束日期,格式为YYYY-MM-DD :return: 不用上班的天数 """ start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d') end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d') days = (end_date - start_date).days + 1 # 总天数加1,包含开始和结束日期 no_work_days = 0 # 第一次停电时间 blackout_date = datetime.datetime.strptime('2021-07-01', '%Y-%m-%d') blackout_days = 2 # 当前停电天数 for i in range(days): date = start_date + datetime.timedelta(days=i) if date.weekday() >= 5: # 周六或周日 continue if date >= blackout_date: # 需要判断是否停电 if blackout_days > 0: blackout_days -= 1 continue else: blackout_date += datetime.timedelta(days=blackout_days+1) # 下一次停电日期 blackout_days += 1 no_work_days += 1 return no_work_days ``` 这个函数接受两个参数 `start_date` 和 `end_date`,分别表示开始和结束日期,返回不用上班的天数。函数的实现过程与前面列出的计算过程类似,使用了 datetime 库来处理日期相关的操作。 可以使用如下的代码来测试这个函数: ```python no_work_days = count_no_work_days('2021-07-01', '2021-08-31') print(f'小明在这段时间内可以不用上班的天数为:{no_work_days}天') ``` 输出结果如下: ``` 小明在这段时间内可以不用上班的天数为:41天 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值