E. Spanning Tree Queries(最小生成树,贪心)

来源场次

Educational Codeforces Round 122 (Rated for Div. 2)

Tag

最小生成树,暴力*2400

题目大意

给一张图,图中有\(n\)个结点\(m\)条边,每条边的权值为\(w\),有\(k\)次询问,每次询问给定一个数值\(x\),对于每一次的\(x\),图中所有边的权值更改为\(\left|w_i-x\right|\),求每次询问的最小生成树的权值

思路

  • 由于题目中的k非常大,不可能每次都去暴力求解一遍最小生成树,因此可以考虑预处理一些关键的最小生成树的值
  • 我们知道对于任意的两条边, \(w_i\)\(w_j\)\((i \neq j)\),当x为它们两个的中点时,这两条边在所有边的排序中将会交换位置,所得到的生成树的值也最有可能会发生改变。因此,我们可以预处理出当\(x\)为任意两条边的中点时的生成树的值
  • 现在的问题是,如果\(x\)的值不在我们认为的理想的点,那我们可以顺带统计,在理想点时位于x左边和位于x右边的值分别有多少个,在\(x\)发生偏移时,我们就可以对这段偏移值的影响估算进去。

AC代码

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0)
#define LL long long
#define maxn (int)(1e6 + 10)



int n , m;
LL w[550] , u[550] , v[550];
int p[550] , r[550];
int find ( int x ) { return p[x] == x ? x : p[x] = find(p[x]) ;}
pair<LL,int> Kruskal (LL xx) {
    LL ans = 0;
    for ( int i = 1 ; i <= n ; i++ ) p[i] = i;
    for ( int i = 1 ; i <= m ; i++ ) r[i] = i;
    sort ( r+1 , r+m+1 , [xx](const int i, const int j){
        LL wa = abs(w[i]-xx);
        LL wb = abs(w[j]-xx);
        if ( wa != wb ) return wa < wb;
        return w[i] > w[j]; // 由于x往右偏移的时候对右边的值更友好,所以相等时右边的值理应更优先
    });
    int cc = 0;
    for ( int i = 1 ; i <= m ; i++ ) {
        int e = r[i] ; int x = find(u[e]) ; int y = find(v[e]);
        if ( x != y ) { 
            ans += abs(w[e]-xx) ; p[x] = y ;
            cc += xx < w[e];
        }
    }
    return {ans,cc};
}

vector<LL> res;
vector<int> cnt;

LL P , k , a , b , c;
int main ()
{
    IOS;
    cin >> n >> m ;
    for ( int i = 1 ; i <= m ; i++ )
    {
        cin >> u[i] >> v[i] >> w[i];
        w[i] *= 2;
    }

    vector<LL> ev(1,0);
    for ( int i = 1 ; i <= m ; i++ )
        for ( int j = i ; j <= m ; j++ )
            ev.push_back(w[i]+w[j] >> 1);
    sort(ev.begin(), ev.end());
    ev.resize(unique(ev.begin(), ev.end()) - ev.begin());

    for ( auto xx : ev )
    {
        auto ss = Kruskal(xx);
        res.push_back(ss.first);
        cnt.push_back(ss.second);
    }

    LL ans = 0 ;
    cin >> P >> k >> a >> b >> c;
    LL x;
    for ( LL cas = 1 ; cas <= k ; cas++ )
    {
        if ( cas <= P ) 
            cin >> x;
        else 
            x = (x * a + b) % c; 

        int pos = upper_bound(ev.begin(), ev.end(), 2*x) - ev.begin() - 1;
        ans ^= ( res[pos] + (n-1-2*cnt[pos]) * 1ll * (2*x - ev[pos])) / 2;
        // 加上左右两边点的个数的差值
    }
    // for ( auto xx : cnt )
    //     cout << xx << " ";
    // cout << endl;
    cout << ans << endl;
}
posted @ 2022-02-28 11:06  I_wanna_be_the_kid  阅读(106)  评论(0)    收藏  举报