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 
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;
}

你可能感兴趣的:(状压,位运算,二进制)