cf 708C - Centroids 树的重心 思维 贪心

20 篇文章 0 订阅
4 篇文章 0 订阅
题目链接

https://codeforces.com/problemset/problem/708/C

题意

给一棵树,你有一次删去任一边,加上任一边(必须维持树结构)的操作机会,问每个点是否可以经过(当然也可以不经过)操作成为重心。

思路

想复杂了

先复习下重心,定义就是题中的,最大子树不超过N/2。重心最少一个,最多为相连两个,所有节点到重心距离和最小。

我们考虑两个重心情况,对不是重心的任一点,我们可以断开重心间边,选择这个点和另一侧重心相连,显然全有解

考虑单个重心情况,容易想到以下贪心做法:选择重心为根跑一下子树结点数,检验当前点时,选取重心不含当前点的最大子树,将其移向自己,检验答案即可。且若一点不可选取,则其重心为根意义下子树都不可选取。

(这边bb下我本来做法,我是列出重心相连子树数组,每次二分的找满足条件的点,属实是想复杂了,好在是过了,不过硬做了一上午,哎,还是菜)
(本质上还是没想清楚移动子树这个操作,分析问题要看本质啊!)

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400500;
	const int inf=0x3f3f3f3f;
	int n,m;
    int sz[maxn],weight[maxn],centroid[2];
    int val[maxn],from[maxn];
    int rt,big1,big2;
    vector<int>e[maxn];
    bool ok[maxn];
    vector<int>sortV;
    //处理重心
    void GetCentroid(int x,int fa){
        sz[x]=1;
        weight[x]=0;
        for(auto y:e[x]){
            if(y!=fa){
                GetCentroid(y,x);
                sz[x]+=sz[y];
                weight[x]=max(weight[x],sz[y]);
            }
        }
        weight[x]=max(weight[x],n-sz[x]);
        if(weight[x]<=n/2){
            centroid[centroid[0]!=0]=x;
        }
    }
    //处理子树权
    void getVa(int x,int fa){
        val[x]=1;
        for(auto y:e[x]){
            if(y==fa)   continue;
            getVa(y,x);
            val[x]+=val[y];
        }
    }
    //处理每个点对应根节点子树编号
    void find_from(int x,int fa,int rt){
        from[x]=rt;
        for(auto y:e[x]){
            if(y==fa)   continue;
            find_from(y,x,rt);
        }
    }
    bool cmp(int a,int b){
        return val[a]<val[b];
    }
    //递归检验
    void dffs(int x,int fa){
        int li=((n-1)/2)+1-val[x];
        if(big1<li) return ;
        else if(val[from[x]]==big1&&big2<li) return ;
        ok[x]=1;
        for(auto y:e[x]){
            if(y==fa)   continue;
            dffs(y,x);
        }
    }
    void chck(){
        rt=centroid[0];
        getVa(rt,-1);
        ok[rt]=1;
        for(auto y:e[rt])
            sortV.push_back(val[y]);
        sort(sortV.begin(),sortV.end());
        big1=sortV[sortV.size()-1];
        big2=sortV[sortV.size()-2];
        for(auto y:e[rt])  
            find_from(y,rt,y);
        for(auto y:e[rt])  
            dffs(y,rt);
        

        for(int i=1;i<=n;i++){
            if(!ok[i])  cout<<'0';
            else        cout<<'1';
            if(i!=n)    cout<<' ';
        }
        cout<<endl;
    }
    int main(){
		IOS
        #ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
        int tn;
        cin>>n;
        for(int i=1;i<n;i++){
            int u,v;
            cin>>u>>v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        GetCentroid(1,-1);
        if(n==2||n==3||centroid[1]){
            for(int i=1;i<=n;i++){
                cout<<1;
                if(i!=n)    cout<<' ';
            }
            cout<<endl;
        }
        else{        
            chck();
        }
        return 0;
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值