AT2167 [AGC006F] Blackout

前言

我是傻逼

正文

神仙题嗷

把原图看成邻接矩阵,(x,y)是黑色的看成从x向y连一条有向边,问题转为求最终有向边的边数。

如果存在边x->y,边y->z, 且边z->x不存在,那么会自动生成边z->x

最终的图是若干个有向三角形和端点不依次相接的边

考虑将各个连通分量三染色,记三种颜色分别为0 1 2
如果一个连通块可以被染成三种颜色,那么所有0和1之间将连边,所有1和2之间将连边,所有2和0之间将连边
对答案的贡献是 c n t 0 ∗ c n t 1 + c n t 1 ∗ c n t 2 + c n t 2 ∗ c n t 0 cnt_0*cnt_1+cnt_1*cnt_2+cnt_2*cnt_0 cnt0cnt1+cnt1cnt2+cnt2cnt0

如果染色有冲突(一个点有两种颜色) 那么 存在长度不等于3或3的倍数的环(包括自环),这个环会被补出全部的有向三元环 ,注意到补完以后的图是完全图,那么他对答案的贡献就是 n 2 n^2 n2

如果染色成功,但只用了两种颜色 那么这是一张二分图,无法加入新边。 对答案的贡献就是原图的边数。

代码dfs一下就行了
一个技巧是 将有向图转为无向图 原图中的每条有向边拆成两条有向边,一条和原图同向的打上1的标记,另外一条打上2的标记,这样染色的时候颜色顺序就不会乱。

#include
using namespace std;
const int maxn = 1e5+7;
#define ll long long
#define mk make_pair
typedef pair<int, int> pii;
int n, m, deg[maxn];
vector<pii> adj[maxn];
vector<int> _;
int col[maxn], vis[maxn], flag;
ll cnt[3], ans;
void dfs(int u) {
    vis[u] = 1; cnt[col[u]]++; _.push_back(u);
    for (int i = 0; i < adj[u].size(); i++) {
        int v = adj[u][i].second;
        if (!vis[v]) {
            col[v] = (col[u] + adj[u][i].first) % 3;
            dfs(v);
        } else {
            if (col[v] != (col[u] + adj[u][i].first) % 3) flag = 1;
        }
    }
}
int main() {
    cin >> n >> m;
    for (int i = 1, x, y; i <= m; i++) {
        cin >> x >> y;
        adj[x].push_back(mk(1, y));
        adj[y].push_back(mk(2, x));
    }
    for (int i = 1; i <= n; i++) {
        if (!vis[i]) {
            flag = 0;
            _.clear();
            cnt[0] = cnt[1] = cnt[2] = 0;
            dfs(i);
            if (flag) ans += (cnt[0]+cnt[1]+cnt[2])*(cnt[0]+cnt[1]+cnt[2]);
            else if (cnt[2]&&cnt[1]&&cnt[0]) ans += cnt[0]*cnt[1]+cnt[1]*cnt[2]+cnt[0]*cnt[2];
            else {
                ll tmp = 0;
                for (int j = 0; j < _.size(); j++) tmp += adj[_[j]].size();
                ans += tmp/2;
            }
        }
    }
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(atcoder,图论,建图,icpc,dfs)