【2020杭电多校】Total Eclipse 并查集+思维

题目链接:Total Eclipse

 

题意:

t组输入,给你一个由n个点,m条边构成的图,每一个点的权值是ai。你每一次可以选择一批联通的点,然后让他们的权值都减去1。问最后把所有点的权值都变成0需要多少次操作

 

题解:

最简单的思路就是每一次dfs找到图中联通的最长的链,然后让这条链减去链上所有点中那个最小的权值。之后这条链就断了。然后再去找链,,依次。。但是这样复杂度太高了,就会TLE

 

如果每一次找到的链上所有点的权值是有序的,那么我们就可以一次得到将这条链上的点的权值变成0的最小操作数,比如下面一条权值链

9->8->7->6->2->1,那么把这些点权值变成0只需要9次

 

那么我们可以从新构建一下这个图,我们可以按照这些点的权值从大到小排序,然后遍历它,先找到一个点,那么这个点就独自构成一个连通块,如果下一个点和之前点相连,那么这两个点就在一个连通块上,连通块数量不加1,否则连通块数量加1(如果这个点将多个连通块相连,那么连通块个数就不止减1了)。我们先说连通块数量减1情况,我们的答案要加上(a[2]-a[3])*ans (ans是连通块数量,a数组就是题目上的权值数组),他代表的意思就是a[2]点相对于a[3]点要多做几次操作,乘于ans是,有ans个连通块都需要多做a[2]-a[3]次操作。(在加入第一个点的时候也要进行这样的操作)

 

模拟过程可以看一下这篇博客:https://blog.csdn.net/yangzijiangac/article/details/107559199

代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <math.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
vector<ll>w[maxn];
ll n,m,v[maxn],vis[maxn];
struct shudui
{
    ll pos,val;
}a[maxn];
bool mmp(shudui x,shudui y)
{
    return x.val>y.val;
}
ll finds(ll x)
{
    if(x!=v[x])
    {
        ll y=finds(v[x]);
        v[x]=y;
        return y;
    }
    return x;
}
int main()
{
    ll t;
    scanf("%lld",&t);
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        scanf("%lld%lld",&n,&m);
        for(ll i=1;i<=n;++i)
            w[i].clear();
        for(ll i=1;i<=n;++i)
        {
            v[i]=i;
            scanf("%lld",&a[i].val);
            a[i].pos=i;
        }
        sort(a+1,a+1+n,mmp);
        a[n+1].val=0;
        while(m--)
        {
            ll x,y;
            scanf("%lld%lld",&x,&y);
            w[x].push_back(y);
            w[y].push_back(x);
        }
        ll sum=0,ans=0;
        for(ll i=1;i<=n;++i)
        {

            ans++;
            ll x=a[i].pos;vis[x]=1;
            for(ll j=0;j<w[x].size();++j)
            {

                ll pos=w[x][j];
                if(vis[pos])
                {
                    ll fx=finds(x);
                    ll fy=finds(pos);
                    if(fx!=fy)
                    {
                        v[fx]=fy;ans--;
                    }
                }
            }
            sum=sum+ans*(a[i].val-a[i+1].val);
        }
        printf("%lld\n",sum);
    }
    return 0;
}

 

posted @ 2020-07-25 12:10  kongbursi  阅读(179)  评论(0编辑  收藏  举报