Codeforces Round 891 (Div. 3)

E

题意:

给你一个数组a = [\(x_1\) , \(x_2\) , \(x_3\) ... \(x_n\) ], 对于每一个数\(x_i\), 我们带入数组中都可以得到n个区间([min(\(x_i\), \(x_j\)), max(\(x_i\), \(x_j\))])
然后定义函数f(x)(x \(\in[1, 10^{9}]\))为包含x的区间个数,输出每个\(x_i\)对于的f(x)的和

思路:

首先要明白对于个\(x_i\)而言,它的f(x)之和其实就是区间长度之和,因为对于一个区间[l, r]来说,内部的f(x)一定会加一, 所以我们先将数组排序
所以\(\sum{f(x)}\) = (\(x_i\) - \(x_1\) + 1) + (\(x_i\) - \(x_2\) + 1) + .... +(\(x_i\) - \(x_i\) + 1) + (\(x_{i + 1}\) - \(x_{i}\) + 1) + (\(x_n\) - \(x_i\) + 1) = i * \(x_i\) - \(\sum_{j = 1}^{j = i}{x_j}\) + \(\sum_{j = i + 1}^{j = n}{x_j}\) - (n - i) * \(x_i\) + n

void solve() 
{
    int n; cin >> n;
    std::vector<pair<LL, LL>> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i].first;
        a[i].second = i;
    }

    sort(a.begin(), a.end());
    
    vector<LL> s(n + 1);
    for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i].first;

    std::vector<LL> ans(n + 1);
    for (int i = 1; i <= n; i++)
    {
        ans[a[i].second] = i * a[i].first - s[i] + (s[n] - s[i]) - (n - i) * a[i].first + n;
    }
    
    for (int i = 1; i <= n; i++)
        cout << ans[i] << ' ';
    cout << endl;

}

F

题意:

已知数组a, 寻找满足\(a_i + a_j = x\), \(a_i * a_j = y\) 的数对(i, j)有多少?

思路:

因为\(x^{2}\) = \((a_i + a_j)^{2}\), \(x^{2}\) - 4 * y = \((a_i - a_j)^{2}\)
所以 直接解方程就行

inline void solve(){
    int n, q; cin >> n;
    map<LL,int>mp;
    for(int i = 1; i <= n; i++){
        int x; cin >> x;
        mp[x] ++;
    }

    cin >> q;
    while (q--)
    {
        LL x, y;
        cin >> x >> y;
        
        if (x * x - 4 * y < 0) {
            cout << 0 << " ";
            continue;
        }
        
        LL s = sqrt(x * x - 4 * y);
        LL a = (x + s) / 2;
        LL b = (x - s) / 2;
        if (a + b != x || a * b != y) {
            cout << 0 << " ";
            continue;
        }
        if (a == b) {
            int c = mp[a];
            cout << 1LL * c * (c - 1) / 2 << " ";
        } else {
            cout << 1LL * mp[a] * mp[b] << " ";
        }
    }
    cout << '\n';
}

G

题意:

给你一棵树,你可以在树上加边,问有多少种加法使得加边后的图的最小生成树依然是给出的那棵树,新图的边权不能大于S

思路:

我们参照Kruskal算法重构图,Kruskal每次都贪心的选离集合最近的那一条边,这启发我们如果想在边(u, v, w)上加一条边,那么这条边的大小一定是属于[w + 1, s]的,因为如果加的边是小于w的,那么最小生成树在选边的时候会选小的那条从而导致生成树发生改变,因为生成树的每一条边都已知,所以对于每一条边而言贡献值为\((s - w + 1)^{size[u] * size[v] - 1}\), size表示当前点所在连通块的大小,分别从一条边的左右两边选一个点出来连接,s - w 为这条边的取值范围然后加上不连的情况,
size[u] * size[v] - 1为点的选择情况,要除开(a, b)这条本身存在的边

inline void solve() 
{
    int n, s; cin >> n >> s;
    vector<tuple<int, int, int>> adj;

    for (int i = 1; i < n; i++)
    {
        int u, v, w; cin >> u >> v >> w;
        adj.push_back({u, v, w});
    }

    sort(adj.begin(), adj.end(), cmp);

    DSU dsu(n + 1);
    LL ans = 1;
    for (auto [u, v, w] : adj)
    {
        ans = (ans * qmi(s - w + 1, (LL)dsu.size(u) * dsu.size(v) - 1)) % MOD;

        dsu.merge(u, v);
    }

    cout << ans << endl;
}
posted @ 2023-08-10 15:45  自动机  阅读(15)  评论(0)    收藏  举报