三十年河东,三十年河西|

自动机

园龄:1年10个月粉丝:2关注:4

Codeforces Round 891 (Div. 3)

E

题意:

给你一个数组a = [x1 , x2 , x3 ... xn ], 对于每一个数xi, 我们带入数组中都可以得到n个区间([min(xi, xj), max(xi, xj)])
然后定义函数f(x)(x [1,109])为包含x的区间个数,输出每个xi对于的f(x)的和

思路:

首先要明白对于个xi而言,它的f(x)之和其实就是区间长度之和,因为对于一个区间[l, r]来说,内部的f(x)一定会加一, 所以我们先将数组排序
所以f(x) = (xi - x1 + 1) + (xi - x2 + 1) + .... +(xi - xi + 1) + (xi+1 - xi + 1) + (xn - xi + 1) = i * xi - j=1j=ixj + j=i+1j=nxj - (n - i) * xi + 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, 寻找满足ai+aj=x, aiaj=y 的数对(i, j)有多少?

思路:

因为x2 = (ai+aj)2, x2 - 4 * y = (aiaj)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的,那么最小生成树在选边的时候会选小的那条从而导致生成树发生改变,因为生成树的每一条边都已知,所以对于每一条边而言贡献值为(sw+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;
}

本文作者:自动机

本文链接:https://www.cnblogs.com/monituihuo/articles/17619286.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   自动机  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起