• 【2020.11.30提高组模拟】删边(delete) 题解


    【2020.11.30提高组模拟】删边(delete) 题解

    题意简述

    给一棵树删边,每次删的代价为这条边所连的两个点的子树中最大点权值。

    求删光的最小代价。

    (nle100000).

    Solution

    正着思考发现没有什么好的思路,贪心的话会后效性。不妨反过来考虑

    这时题目变成了:给(n)个点,每次连通两个点集,代价为两个点集中最大点权之和。

    例如这个图

    image-20201130125545031

    首先,每个点都是独立的。

    那么你会先加入(1-2)还是(2-3)呢?

    如果先加入(2-3),代价为(2+3),接下来再加入点(1)时,(2-3)所产生的贡献是(3)

    如果而后还有一些集合需要并进来时,当前集合所产生的贡献为(3),很大很浪费。

    所以,我们要让点权大的点以后再合并,点权小的点先合并。

    所以初始化时先把边权设为其所连两点的点权中更大的那一个。对所有边按照边权排序,再用类似并查集的方法合并同时维护集合中的最大点权。

    以上。

    Code

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<map>
    #include<set>
    #include<queue>
    #include<vector>
    #define IL inline
    #define re register
    #define LL long long
    #define ULL unsigned long long
    #ifdef TH
    #define debug printf("Now is %d
    ",__LINE__);
    #else
    #define debug
    #endif
    using namespace std;
    
    template<class T>inline void read(T&x)
    {
    	char ch=getchar();
    	int fu;
    	while(!isdigit(ch)&&ch!='-') ch=getchar();
    	if(ch=='-') fu=-1,ch=getchar();
    	x=ch-'0';ch=getchar();
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	x*=fu;
    }
    inline int read()
    {
    	int x=0,fu=1;
    	char ch=getchar();
    	while(!isdigit(ch)&&ch!='-') ch=getchar();
    	if(ch=='-') fu=-1,ch=getchar();
    	x=ch-'0';ch=getchar();
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*fu;
    }
    int G[55];
    template<class T>inline void write(T x)
    {
    	int g=0;
    	if(x<0) x=-x,putchar('-');
    	do{G[++g]=x%10;x/=10;}while(x);
    	for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('
    ');
    }
    int n,f[100010],mx[100010],a[100010];
    LL ans;
    int getf(int x)
    {
    	if(f[x]==x) return x;
    	return f[x]=getf(f[x]);
    }
    void merge(int x,int y)
    {
    	x=getf(x);
    	y=getf(y);
    	if(x!=y)
    	{
    		ans+=mx[x]+mx[y];
    		mx[x]=max(mx[x],mx[y]);
    		f[y]=x;
    	}
    }
    struct edge
    {
    	int x,y;
    	edge(int xx=0,int yy=0){x=xx,y=yy;}
    //	int v()const{return max(a[x],a[y]);}
    	bool operator<(const edge & z)const
    	{
    		return max(a[x],a[y])<max(a[z.x],a[z.y]);
    	}
    };
    vector<edge>e;
    int main()
    {
    //	freopen("delete.in","r",stdin);
    //	freopen("delete.out","w",stdout);
    	n=read();
    	for(int i=1;i<=n;i++) f[i]=i,mx[i]=a[i]=read();
    	for(int i=1;i<n;i++) e.push_back(edge(read(),read()));
    	sort(e.begin(),e.end());
    	for(unsigned i=0;i<e.size();i++)
    	{
    		merge(e[i].x,e[i].y);
    	}
    	cout<<ans;
    	return 0;
    }
    

    End

    差点抱玲了(玲酱这么可爱为什么不抱抱呢?大雾),还好最后想到了逆向思维。

    考试之后,( exttt{lc})的算法是(O(n))的,让我大开眼界呢!

    评测机出锅了,image-20201130130909649

    image-20201130130932477

    仿佛回到了去年暑假的那些日子呢

    image-20201130131020655


    不会吧不会吧,不会是个人写的都是(O(n))吧!

  • 相关阅读:
    win10 ,本地连接无法识别网络 ,无线正常,
    vba 声音
    win10 优化
    比较火和常用的命令
    手机电脑平板 查图纸、查点位图、查通病、自学维修知识等通通都有的工具
    e4a mysql
    e4a 对话框的 多选单选颜色日期时间
    e4s 文本操作 数组操作
    e4a sqlite案例
    e4a-窗口切换
  • 原文地址:https://www.cnblogs.com/send-off-a-friend/p/14060738.html
Copyright © 2020-2023  润新知