Link
我们首先考虑得到 S n S_n Sn 之后如何统计答案。那么实际上也就是问最多可以组成多少数对,数对中两个数字不能相同。那么首先设数组中出现次数最多的数字的出现次数为 c n t cnt cnt。
若 2 × c n t ≤ n 2×cnt≤n 2×cnt≤n ,则每个元素都能找到配对,答案就是 n n n 。证明如下:
我们让相同的元素都摆在一起:如: [ 7 , 7 , 7 , 2 , 2 , 2 , 6 , 6 ] [7,7,7,2,2,2,6,6] [7,7,7,2,2,2,6,6] ,那么我们把所有元素都向右循环位移 c n t cnt cnt 位去两两配对,变成 [ 2 , 6 , 6 , 7 , 7 , 7 , 2 , 2 ] [2,6,6,7,7,7,2,2] [2,6,6,7,7,7,2,2] ,也就是另所有元素下标(从 0 0 0 开始)都变成 ( i + c n t ) % n (i+cnt)\%n (i+cnt)%n 。那么首先对于出现次数最多的数字来说,最左端一定正好移出去,最右端一定无法循环移回来( ∵ 2 × c n t ≤ n ∵2×cnt≤n ∵2×cnt≤n),所以出现次数最多的元素一定可以匹配上不同的元素。那么再考虑出现次数不是最多的元素,对于位移时不会越过 n n n 的元素,其走了 c n t cnt cnt 步,所以其肯定不会在自己的区域内,对于循环位移会越过 n n n 的元素,其想回到自己的位置,起码得越过出现次数最多的元素,所以至少都得走 c n t + 1 cnt+1 cnt+1 步,所以其也不可能配对到与自己相同的元素。综上所述,在 2 × c n t ≤ n 2×cnt≤n 2×cnt≤n 时,将每个元素都循环位移 c n t cnt cnt 位,所有元素都可以配对到不同的数字。
若 2 × c n t > n 2×cnt>n 2×cnt>n ,那么结果就是 2 × ( n − c n t ) 2×(n-cnt) 2×(n−cnt) ,也就是将每个非众数的数字和众数匹配。这个可以这么证明,最终的匹配的每个数对中一定至少消耗一个非众数(毕竟相同的数字无法匹配,众数无法与自己匹配),那么最优情况下,也就是每个数对只消耗一个非众数,也就是出现 n − c n t n-cnt n−cnt 个数对,那么这种情况是否可以达到呢,显然是可以的,我们只要让每个非众数与众数配对即可,所以最后结果就是 2 × ( n − c n t ) 2×(n-cnt) 2×(n−cnt) 。
那么有这个结论之后,我们就只要统计出元素出现次数的最大值即可,那么其实对于这个题我们可以求出 S n S_n Sn 所有元素的出现次数。我们实际上就是要统计与 S n S_n Sn 有关的 1 1 1 类型节点被加和过多少次。我们可以对于第二个操作建边,一个元素被加和过多少次,也就是其父节点的被加和次数的和,那么处于最顶端的 S n S_n Sn 自然被认为加和了一次。这里元素个数肯定是有限的,所以不可能出现环路,我们可以拓扑排序后记录 S n S_n Sn 在拓扑序的位置,去进行如下代码的 D P DP DP :
d[n] = 1;
for (int i = idx; i < (int)tp.size(); ++i) {
int u = tp[i];
if (!d[u]) continue;
for (auto v : g[u]) {
d[v] += d[u];
}
}
这样就能保证完整地计算到每个节点被加和的次数,那么也就自然求得了 S n S_n Sn 中每个元素的出现次数,自然就可以求得答案。另外,用这个方法感觉常数比较大,加个超级快读才能过。。。
c o d e code code
#include
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
unordered_map<int, int> id;
int n, in[N], tot, vis[N];
ll cnt[N], d[N];
queue<int> q;
vector<int> g[N];
vector<int> a[N], tp, leaves;
static char buf[100000],*pa=buf,*pd=buf;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read()
{
register int x(0);register char c(gc);
while(c<'0'||c>'9')c=gc;
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x;
}
void dfs(int u) {
vis[u] = 1;
for (auto v : g[u]) {
in[v]++;
if (!vis[v]) {
dfs(v);
}
}
}
void topo() {
q.push(n); tp.push_back(n);
while(!q.empty()) {
int u = q.front(); q.pop();
for (auto v : g[u]) {
in[v]--;
if (!in[v]) {
q.push(v);
tp.push_back(v);
}
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int T;
T = read();
while(T--) {
n = read();
for (int i = 1; i <= n; ++i) {
int op, k, q, x, y;
op = read();
if (op == 1) {
k = read(); leaves.push_back(i);
for (int j = 1; j <= k; ++j) {
int x; x = read();
if (!id[x]) id[x] = ++tot;
a[i].push_back(id[x]);
}
}
else {
x = read(); y = read();
g[i].push_back(x);
g[i].push_back(y);
}
}
dfs(n);
topo();
d[n] = 1;
for (int i = 0; i < (int)tp.size(); ++i) {
int u = tp[i];
if (!d[u]) continue;
for (auto v : g[u]) {
d[v] += d[u];
}
}
for (int i = 0; i < (int)leaves.size(); ++i) {
for (auto j : a[leaves[i]]) {
cnt[j] += d[leaves[i]];
}
}
long long ans = 0, pre = 0;
ll sum = 0, mx = 0;
for (int i = 1; i <= tot; ++i) {
mx = max(mx, cnt[i]);
sum += cnt[i];
}
if (mx*2 >= sum) {
printf("%lld\n", 2ll * (sum - mx));
}
else {
printf("%lld\n", sum);
}
tp.clear();
id.clear();
leaves.clear();
for (int i = 1; i <= tot; ++i) {
cnt[i] = 0;
}
for (int i = 1; i <= n; ++i) {
d[i] = 0;
a[i].clear();
g[i].clear();
in[i] = 0;
vis[i] = 0;
}
tot = 0;
}
}