2020 CCPC-Wannafly Winter Camp Day5 Div.1&2——A Alternative Accounts【状压】

题目传送门


题目描述

Everybody knows that jiry_2 = Syloviaely.
There are {n}n different accounts on the website, and some of them competed in the recent {k}k contests. However, Mike suspects that there are lots of alternative accounts.
There are axioms believed by everyone that nobody can use two different in one contest simultaneously and each account can be owned by only one person. So different accounts without overlapping contest participation can be owned by the same person.
Mike wants to know the minimum possible number of different people behind these accounts.


输入描述:

The first line contains two integers n , k   ( 1 ≤ n ≤ 1 0 5 , 1 ≤ k ≤ 3 ) n, k~(1\leq n\leq 10^5, 1\leq k\leq 3) n,k (1n105,1k3).
Each of the following k {k} k lines contains an integer m   ( 1 ≤ m ≤ n ) m~(1\leq m\leq n) m (1mn)
first, followed by m {m} m distinct integers x i   ( 1 ≤ x i ≤ n ) x_i~(1\leq x_i\leq n) xi (1xin)
indicating the accounts participating the contest.
Some accounts may not participate in any contests.


输出描述:

Output one integer - the answer.


输入

5 3
2 1 2
3 2 3 4
4 4 5 1 2


输出

4


题意

  • 给你 n n n 个账号,有 k k k 场比赛,给出每场比赛出现的账号,求最少可能有多少人。每个人可以使用不同账号,一个账号只能被一个人使用。

题解

  • 发现 k < = 3 k<=3 k<=3,就很简单了。考虑 k = 3 k=3 k=3 的情况即可
  • 对于三场都参加的账号,必定每个账号都是独立的一个人使用
  • 稍加分析可以发现,如果人 a a a 出现在了第 1 1 1 场和第 2 2 2 场,那么他还可能参加的其他账号,只能是只参加第 3 3 3 场的账号
  • 利用二进制状态压缩,对于每一个账户, s t a t e [ i ] state[i] state[i] 记录这个账户参赛情况 ( 0 : 未 参 赛 ; 1 , 2 , 4 : 只 参 加 了 第 1 , 2 , 3 场 ; 3 ( 011 ) : 参 加 第 1 、 2 场 ; 5 ( 101 ) : 参 加 第 1 、 3 场 ; 6 ( 110 ) : 参 加 第 2 、 3 场 ; 7 ( 111 ) 全 都 参 加 ) (0:未参赛;1,2,4:只参加了第1,2,3场;3(011):参加第1、2场;5(101):参加第1、3场;6(110):参加第2、3场;7(111)全都参加) 0:1241233(011)125(101)136(110)237(111)
  • n u m [ i ] : num[i]: num[i]记录不同参赛情况的人数

AC-Code

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;

int state[maxn];
int num[8];//12(011);13(101);23(110);123(111)
int main() {
	int n, k;
	while (cin >> n >> k) {
		for (int i = 0; i < k; ++i) {
            int t;
			cin >> t;
			for (int j = 0; j < t; ++j) {
				int temp;
				cin >> temp;
				state[temp] |= (1 << i);
			}
		}
		for (int i = 1; i <= n; ++i)	++num[state[i]];
		int ans = num[7]; // 参加三天的账号数
		for (int i = 0; i < 3; ++i) {
			int x = 7 ^ (1 << i);// 参加两天
			ans += num[x]; // 加上参加两天的人数
			if (num[x] <= num[1 << i])	// 一天往两天合并(反之也可)
				num[1 << i] -= num[x];
			else
				num[1 << i] = 0;
		}
		ans += max(num[1], max(num[2], num[4])); // 最后加上未被合并的最大账号数
		cout << ans << endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值