hdu4424 并查集+贪心+思维

#include<bits/stdc++.h>
#define MAXN 200005
using namespace std;

long long n,tot;
long long f[MAXN],cnt[MAXN],ans[MAXN];
struct node{
    long long from,to,cost,next,tp;
}e[MAXN<<1]; 

void init(){
    tot=0;
    memset(ans,0,sizeof(ans));
    for(int i=1;i<=n;i++)f[i]=i,cnt[i]=1;
}

void add(long long x,long long y,long long z){
    tot++;
    e[tot].from=x;
    e[tot].to=y;
    e[tot].cost=z;
}

long long found(long long x){
    if(f[x]==x)return x;
    int fa=found(f[x]);
    f[x]=fa;
    return fa;
}

bool cmp(node x,node y){
    return x.cost>y.cost;
}

int main(){
    while(scanf("%lld",&n)==1){
        init();
        for(int i=1;i<n;i++){
            long long x,y,z;
            scanf("%lld%lld%lld",&x,&y,&z);
            add(x,y,z);    
        }
        sort(e+1,e+1+tot,cmp);
        for(int i=1;i<n;i++){
            long long ff=found(e[i].from),fy=found(e[i].to);
            if(ff!=fy){
                if(ans[fy]+cnt[ff]*e[i].cost>ans[ff]+cnt[fy]*e[i].cost)swap(ff,fy);
                ans[ff]+=(cnt[fy]*e[i].cost);
                cnt[ff]+=cnt[fy];
                f[fy]=ff;
            }
        }
        cout<<ans[found(1)]<<endl;
    }
}
View Code

一开始有点想法....

.................. 

嘛,仔细分析一下,发现可以在最小值最大哪里搞一下.....

尝试从小到大枚举每条边....发现这样答案可以计算(就是一个分治的过程....每次选择与这条边相连的两点(怎么说呢)就是舍去一段子树....差不多了吧)

或者贪心好像也行把....每次把节点数小的给删掉....(显然满足题目要求把....).

然后考虑怎么解决一条边是否已经再被删掉的子树里面出现....

直接并查集维护一下每个边左右子树的根....要删掉那一个就把它的根标记一下就好了.....但是不能路径压缩,,,,(显然把....)

但是,,上面做法会t飞

于是乎,再度分析...

不如从最大值考虑?

于是乎,,,,,,发现,一些较大边对于答案没有贡献啊!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

那么我们要它干嘛,直接删掉.....考虑删掉后咋办....把两个点合并呗.....

对于没有贡献的边怎么合并都无所谓啦......

但是对于有贡献的就要考虑,既然是要最大化答案,,,,,当然是按照可以使得答案最大的来搞(感觉自己好多废话)

于是乎就清晰了

1 从大到小排序

2 然后依次合并点集....

然后这题就结束了

------------------------------------------------------------------------------------------------------------------------------------------------------------

经验:

1 仔细分析题目啊!!!!!!!

2 遇到难搞(会炸)的地方要考虑影藏性质

3.不能应为感觉思考出一个假的正解(没AC)就沾沾自喜 去颓