The 2019 China Collegiate Programming Contest Harbin Site E. Exchanging Gifts

E. Exchanging Gifts

Link

The 2019 China Collegiate Programming Contest Harbin Site E. Exchanging Gifts_第1张图片
我们首先考虑得到 S n S_n Sn 之后如何统计答案。那么实际上也就是问最多可以组成多少数对,数对中两个数字不能相同。那么首先设数组中出现次数最多的数字的出现次数为 c n t cnt cnt

2 × c n t ≤ n 2×cnt≤n 2×cntn ,则每个元素都能找到配对,答案就是 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×cntn),所以出现次数最多的元素一定可以匹配上不同的元素。那么再考虑出现次数不是最多的元素,对于位移时不会越过 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×cntn 时,将每个元素都循环位移 c n t cnt cnt 位,所有元素都可以配对到不同的数字。

2 × c n t > n 2×cnt>n 2×cntn ,那么结果就是 2 × ( n − c n t ) 2×(n-cnt) 2×(ncnt) ,也就是将每个非众数的数字和众数匹配。这个可以这么证明,最终的匹配的每个数对中一定至少消耗一个非众数(毕竟相同的数字无法匹配,众数无法与自己匹配),那么最优情况下,也就是每个数对只消耗一个非众数,也就是出现 n − c n t n-cnt ncnt 个数对,那么这种情况是否可以达到呢,显然是可以的,我们只要让每个非众数与众数配对即可,所以最后结果就是 2 × ( n − c n t ) 2×(n-cnt) 2×(ncnt)

那么有这个结论之后,我们就只要统计出元素出现次数的最大值即可,那么其实对于这个题我们可以求出 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;
    }
}

你可能感兴趣的:(图论,思维,算法)