jzoj6495 死星 (竞赛图五元环)

题意

给你一个竞赛图,求其中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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值