题意
给你一个竞赛图,求其中3,4,5元环的个数。
n<=2000
分析
- 3,4元环都很简单。现在来看看这个五元环
- 可以先画几个五元环看看。我拿到这个题有两种思路:像无向图那样按度数算复杂度,或者用bitset进行一些加速。前者很快被我毙掉了,有向图和无向图并不是一回事。
- 竞赛图是本题的一个重要条件。不应该当成稠密的有向图来考虑。
- 尝试直接计算5元环个数无果,按剧本应该进行一些容斥。
- 不如看看五元非环的个数是否好计算。
- 注意到一个五元非环等价于存在入度为2的点,进一步可以发现最多有两个入度=2的点。而且情况简单,无论个数是1还是2均只存在一种形态。
- 于是,先计算出每个五元环或非环内所有入度为2的点数之和。
- 此时,第二类情况被我们计算了两次。并且第二类情况的个数可以通过 O ( n 3 / w ) O(n^3/w) O(n3/w)的操作加上一些非常简单的讨论算出来。
- 有一个要注意的地方就是,你所选的五元环是否有顺逆时针,计数时要上下意义一致。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e3 + 10;
int e[N][N];
ll n, k;
bitset<N> out[N], in[N];
char s[1000010];
int a[N];
ll ans;
int main() {
freopen("deathstar.in", "r", stdin);
// freopen("deathstar.out", "w", stdout);
cin >> n >> k;
scanf("%s",s+1);
for(int i = 1, z = strlen(s + 1), x = 1, y = 2; i <= z; i++) {
int v = s[i] >= 'A' ? 10 + s[i] - 'A' : s[i] - '0';
for(int j = 3; ~j; j--) {
e[x][y] = (v & (1 << j)) != 0;
e[y][x] = !e[x][y];
if (y++ == n) x++, y = x + 1;
}
}
for(int x = 1; x <= n; x++) {
for(int y = 1; y <= n; y++) if (e[x][y]) {
in[y][x] = 1;
out[x][y] = 1;
}
}
if (k == 3) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) if (e[i][j]) {
ans += (in[i] & out[j]).count();
}
}
ans /= 3;
} else if (k == 4) {
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
ans += (ll)(in[i] & out[j]).count() * (out[i] & in[j]).count();
}
}
ans /= 2;
} else {
ans = n * (n - 1) * (n - 2) * (n - 3) * (n - 4) / 10;
for(int i = 1; i <= n; i++) {
ll v = in[i].count();
ans -= v * (v - 1) / 2 * (n - 3) * (n - 4);
}
ll sum = 0;
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
ll z = (in[i] & in[j]).count();
ans += z * (z - 1) * (in[j].count() - e[i][j] - 2);
ans += z * (in[i].count() - e[j][i] - z) * (in[j].count() - e[i][j] - 1);
}
}
}
cout << ans << endl;
}

16万+

被折叠的 条评论
为什么被折叠?



