Save your tears for the day so far away

QOJ 18966

难难结论题,拼尽全力最终猜出。

先考虑一点显然的结论:\(k_i=1\),显然是随便填写的,因此考虑所有的 \(k_i > 1\) 的情况。

先考察样例,不难发现形成的是一个环的状物,因此我们不难得出这样的结论:如果只是一条链,则链的情况为:奇数长度的环,是 \(2len - 1\),否则为 \(2len\)。

然后我们再考察如果不是链的情况,不难证明任意的情况最后都可以转化为若干条链,或者是若干个环,然后上面再挂一些东西。假设我们是在环上的,我们不难发现,如果环上加上一个新的队伍,则这个环就一定每个点都可以产生两个贡献了。再考虑链,我们又能发现如果成链,则左右点一定是只参加一个比赛的队伍加上参加两个比赛的对于,然后我们对于这种情况进行考虑,发现依然是对的。

汇总一下,我们可以猜到结论:如果一个连通块是任意点都可以贡献 \(2\) 的,当且仅当其覆盖的比赛个数为偶数,或者参加的队伍数量多于覆盖的比赛个数,否则就要 \(-2\) 了。然后写个并查集维护下就行了。

思考链非常长的题目!

Code

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 1e6 + 10;
int a[N][5];
int n,m;
int deg[N];
vector<int> vec[N];
int res[N];
void del(int num,int i){
	int oth = 0;
	if(a[num][0] == 1){
		a[num][0] -- ;
		a[num][1] = 0;
	}
	else{
		if(a[num][1] == i) oth = a[num][2];
		else oth = a[num][1];
		a[num][2] = 0;
		a[num][1] = oth;
		a[num][0] -- ;
	}
	return ;
}
int sz[N],cnt[N],fa[N];
int find(int u){
	if(u == fa[u]) return u;
	return fa[u] = find(fa[u]);
}
void merge(int u,int v){
	u = find(u); v = find(v);
	if(u == v) return ;
	fa[v] = u;
	sz[u] += sz[v];
	cnt[u] += cnt[v];
}
int main(){
	IOS;
	cin >> n >> m;
	for(int i=1;i<=m;i++){
		cin >> deg[i];
		for(int j=1;j<=deg[i];j++){
			int x;
			cin >> x;
			vec[i].push_back(x);
			a[x][++a[x][0]] = i;
		}
	}
	for(int i=1;i<=m;i++){
		if(deg[i] == 0) res[i] = 0;
		if(deg[i] == 1){
			res[i] = 1;
			int num = vec[i][0];
			del(num,i);
		}
	}
	for(int i=1;i<=m;i++)
		sz[i] = 1,cnt[i] = 0,fa[i] = i;
	for(int i=1;i<=n;i++){
		if(a[i][0] == 0) continue;
		if(a[i][0] == 1){
			cnt[find(a[i][1])] ++ ;
		}
		if(a[i][0] == 2){
			int u = find(a[i][1]);
			int v = find(a[i][2]);
			cnt[u] ++ ;
			merge(u,v);
		}
	}
	int ans = 0;
	for(int i=1;i<=m;i++){
		ans += res[i];
		if(res[i] == 0 && i == find(i) && cnt[i] > 0){
			if(cnt[i] > sz[i])
				ans += sz[i] * 2;
			else if(cnt[i] == sz[i] && sz[i] % 2 == 0)
				ans += sz[i] * 2;
			else
				ans += sz[i] * 2 - 1;
		}
	}
	cout << ans;
	return 0;
}
posted @ 2026-09-28 14:48  WinterXorSnow  阅读(10)  评论(0)    收藏  举报