CCPC2019哈爾濱站(E - Exchanging Gifts)(拓撲圖+map合併)

CCPC2019哈爾濱站(E - Exchanging Gifts)(拓撲圖+map合併)

time limit per test1 second
memory limit per test512 megabytes
inputstandard input
outputstandard output
judge:點我跳轉

Description

After the dress rehearsal of CCPC Harbin Site 2019, mm contestants are still in the contest arena. They are taking photos, discussing the problems, and exchanging gifts.

Initially, everyone has exactly one gift in their hand. Note that some contestants may have the same type of gifts. Specifically, the type of the gift in the ii-th contestant’s hand can be represented as a positive integer gig_i. Two contestants ii and j (1i,jm)j \ (1\le i,j \le m) share the same type of gifts if and only if gi=gjg_i=g_j holds.

There can be many rounds of gift exchanging between these mm contestants. In each round, two contestants may exchange their gifts with each other. Note that a pair of contestants can exchange gifts multiple times if they like. In the end, there will still be exactly one gift in each contestant’s hand.

Let’s denote hih_i as the type of gift in the ii-th contestant’s hand in the end. If gihig_i\neq h_i holds, the ii-th contestant will be happy, because they have a different type of gift, otherwise they will be unhappy. Your task is to write a program to help them exchange gifts such that the number of happy contestants is maximized. For example, if g=[3,3,2,1,3]g=[3,3,2,1,3] and h=[1,2,3,3,3]h=[1,2,3,3,3], there will be 44 happy contestants.

Since mm can be extremely large, you will be given nn sequences s1,s2,,sns_1,s_2,\dots,s_n, and the sequence gg is equal to sns_n. The ii-th (1in)(1\le i \le n) sequence will be given in one of the following two formats:

“1 k q[1…k]” (1k1061\leq k\leq 10^6, 1qi1091\leq q_i\leq 10^9): It means si=[q1,q2,,qk]s_i=[q_1,q_2,\dots,q_k].
“2 x y” (1x,yi11\leq x,y\leq i-1): It means si=sx+sys_i=s_x+s_y. Here “++” denotes concatenation of sequences, for example [3,3,2]+[2,2,3,3]=[3,3,2,2,2,3,3][3,3,2]+[2,2,3,3]=[3,3,2,2,2,3,3].

Input

The input contains multiple cases. The first line of the input contains a single integer TT (1T100001 \leq T \leq 10\,000), the number of cases.

For each case, the first line of the input contains a single integer nn (1n1061 \leq n \leq 10^6), denoting the number of sequences. Each of the next nn lines describes a sequence in one of the two formats defined in the problem statement, where the ii-th (1in1\le i \le n) line describes the sequence sis_i.

It is guaranteed that the sum of all nn over all cases does not exceed 10610^6, and the sum of kk over all cases does not exceed 10610^6. It is also guaranteed that no sequence has a length that exceeds 101810^{18}.

Output

For each case, print a single line containing a single integer denoting the maximum number of happy contestants.

Example

input

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

output

4
6

題解

按照慣例,先奉圖一張:
在這裏插入圖片描述
在這裏插入圖片描述
題意有點繞,大概是這樣:

就是說如果有一個序列經過重新排列後,一個數字和以前這個位子的數字不同的話,快樂值就會+1,問你一個序列最大的快樂值是多少。其實很簡單,我們只需要判斷出現次數最多的那個數字的次數 mm 有沒有超過 n/2n/2 即可,如果沒有,那麼經過重新排列後每個位子的數字都可以和以前的數字不同,快樂值就是 nn ;如果超過了,那麼最大的快樂值就會是 2n2m2n-2m

可是問題出在哪了呢?就是題目不直接給你那個序列,因爲它太長了,最大長度可以是 101810^{18}……

題目會給你 nn 個序列,你要求的是第 nn 個序列(即 s[n1]s[n-1] )的快樂值;有的序列是直接告訴你元素的,有的序列是由前面的某兩個序列合併在一起組成的,這道題難就難在如何高效地“合併”這些序列,最終求得目標序列。

當然,最直接的思路肯定是用 mapmap 維護,然後 mapmap 暴力維護 TT 在了 test2test2 .

優化一下合併的過程,從傳對象變成傳指針,一切操作都在已有的對象上進行, TT 在了 test3test3 .

改變思路,從正向合併變成反相遞推,建立一個圖進行 dfsdfs 搜索需要哪些基本的序列,找完之後統一合併, TT 在了 test4test4 .

不再暴力 dfsdfs 了,改成求拓撲圖,先 bfsbfs 找出所有有用的序列的入度,這個“有用”指的是被用來合併最後一個序列的序列。然後再 bfsbfs 求拓撲圖,中間合併這些序列, TT 在了 test5test5 .

最後換了一個快讀板子, AA 了(手動滑稽)。

下面講解一些如何建立拓撲圖和如何求解拓撲圖。

假如這裏有一些序列,序列n是由序列4和序列3構成的,序列n-1是由序列4和序列2構成的,序列3是由序列1和序列2構成的。好複雜的樣子……

但是畫成圖就好看多了:

在這裏插入圖片描述
可以看到,真實的序列只有1、2和4 ,其他的都是需要組合而成的;而仔細觀察發現雖然序列n-1也是一個序列,但是它對序列n的合成沒有貢獻。所以我們認爲它對4和2的入讀時無效的;我們只統計有效的入讀之後,圖是這樣的:
在這裏插入圖片描述
從序列n開始往下bfs搜索,往下尋找的深度即是這個序列的重用次數;只有當路徑上的點的入讀爲0時我們才push這個點繼續往前走。最後就完成了一個拓撲圖的求解。而每一次合併兩個序列我們只需要更新一下map的數字出現的次數即可,最後我們從map裏統計出現次數最多的那個數字。

代碼

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define maxn 1000006
#define _for(i, a) for(LL i = 0; i < (a); ++i)
#define _rep(i, a, b) for(LL i = (a); i <= (b); ++i)
using namespace std;
typedef long long LL;

inline char nc() {
	static char buf[1000000], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
}
template <typename _Tp> inline void read(_Tp&sum) {
	char ch = nc(); sum = 0;
	while (!(ch >= '0'&&ch <= '9')) ch = nc();
	while (ch >= '0'&&ch <= '9') sum = (sum << 3) + (sum << 1) + (ch - 48), ch = nc();
}

struct poi {
	LL l, r;
	poi() {}
	poi(LL l, LL r) :l(l), r(r) {}
};

vector< vector<LL> > vm;
poi G[maxn];
LL T, n;
map< LL, LL > an;
LL vis[maxn], cnt[maxn], in[maxn];

void init() {
	vm.clear();
	an.clear();
	_for(i, n) cnt[i] = 0, vis[i] = 0, in[i] = 0;
}

void bfs_in(LL s) {
	queue<LL> q;
	q.push(s);
	vis[s] = 1;
	while (q.size()) {
		LL t = q.front();
		q.pop();
		if (G[t].r != -1) {
			LL l = G[t].l, r = G[t].r;
			in[l]++; in[r]++;
			if (!vis[l]) q.push(l), vis[l] = 1;
			if (!vis[r]) q.push(r), vis[r] = 1;
		}
	}
}

void bfs(LL s) {
	queue<LL> q;
	cnt[s] = 1;
	q.push(s);
	while (q.size()) {
		LL t = q.front();
		q.pop();
		if (G[t].r != -1) {
			LL l = G[t].l, r = G[t].r;
			cnt[l] += cnt[t];
			cnt[r] += cnt[t];
			if (--in[l] == 0) q.push(l);
			if (--in[r] == 0) q.push(r);
		}
		else {
			for (LL &x : vm[G[t].l]) {
				an[x] += cnt[t];
			}
		}
	}
}

void sol() {
	init();
	_for(i, n) {
		LL op;
		read(op);
		poi t;
		if (op == 1) {
			LL len, x;
			read(len);
			G[i].l = vm.size();
			G[i].r = -1;
			vm.resize(vm.size() + 1);
			_for(j, len) {
				read(x);
				vm.back().emplace_back(x);
			}
		}
		else {
			LL x, y;
			read(x), read(y);
			G[i].l = x - 1;
			G[i].r = y - 1;
		}
	}
	bfs_in(n - 1);
	bfs(n - 1);
	LL ans = 0, num = 0;
	for (auto &it : an) {
		num += it.second;
		ans = max(ans, it.second);
	}
	printf("%lld\n", ans > num / 2 ? 2 * num - 2 * ans : num);
}

int main() {
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	//freopen("in.txt", "r", stdin);

	while (cin >> T) {
		_for(i, T) {
			read(n);
			sol();
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章