zoukankan      html  css  js  c++  java
  • dtoj#4178. 配对(pair)

    题目描述:

    由于 zzq 太懒了,所以这题没有题目背景。

    有一棵树,树上有 n 个点,每条边上有一个非负边权。

    在这 n 个点中有 k 个特殊点,其中 k 为偶数。定义两个点的距离为它们在树上的简单路径上的边权之和。你需要将这 k 个点配成k/2个互不相交的对,并最大化每一对点的距离之和。

    思路:

    一个结论,这k条路经至少会交于一个公共点,可以证明如果没有交于一点一定存在一个更优的方案并且交于一点。

    于是考虑去求所有选中的点到相交点的距离,动态维护距离,同时要保证拥有最多选中点的子树大小<=k/2,否则会有路径无法交于相交点。

    以下代码:

    #include<bits/stdc++.h>
    #define il inline
    #define LL long long
    #define _(d) while(d(isdigit(ch=getchar())))
    using namespace std;
    const int N=1e5+5;
    bool f[N];
    LL ans,dist[N],S;
    int dfn[N],id,sz[N],mx[N],sum[N],out[N],rt,a[N];
    int n,k,head[N],ne[N<<1],to[N<<1],w[N<<1],cnt;
    il int read(){
        int x,f=1;char ch;
        _(!)ch=='-'?f=-1:f;x=ch^48;
        _()x=(x<<1)+(x<<3)+(ch^48);
        return f*x;
    }
    il void insert(int x,int y,int z){
        ne[++cnt]=head[x];head[x]=cnt;
        to[cnt]=y;w[cnt]=z;
    }
    il void dfs1(int x,int fa){
        dfn[x]=++id;sz[x]=f[x];
        sum[id]=sum[id-1]+f[x];
        if(f[x])S+=dist[x];
        for(int i=head[x];i;i=ne[i]){
            if(to[i]==fa)continue;
            dist[to[i]]=dist[x]+w[i];
            dfs1(to[i],x);
            sz[x]+=sz[to[i]];
            mx[x]=max(mx[x],sz[to[i]]);
        }
        mx[x]=max(mx[x],k-sz[x]);
        out[x]=id;
    }
    il void dfs2(int x,int fa){
        if(mx[x]*2<=k&&ans<S)ans=S,rt=x;
        for(int i=head[x];i;i=ne[i]){
            if(fa==to[i])continue;
            int tot=sum[out[to[i]]]-sum[dfn[to[i]]-1];
            S+=1ll*(k-(tot<<1))*w[i];
            dfs2(to[i],x);
            S-=1ll*(k-(tot<<1))*w[i];
        }
    }
    int tt=0;
    vector<int> v[N];
    struct node{
        int x,d,id;
        bool operator<(const node&t1)const{
            return d<t1.d;
        }
    };
    priority_queue<node> q;
    il void dfs3(int x,int fa){
        if(f[x])v[tt].push_back(x);
        for(int i=head[x];i;i=ne[i]){
            if(fa==to[i])continue;
            dfs3(to[i],x);
        }
    }
    int main()
    {
        n=read();k=read();
        for(int i=1;i<n;i++){
            int x=read(),y=read(),z=read();
            insert(x,y,z);insert(y,x,z);
        }
        for(int i=1;i<=k;i++)a[i]=read(),f[a[i]]=1;
        dfs1(1,0);dfs2(1,0);
        if(f[rt])v[++tt].push_back(rt);
        for(int i=head[rt];i;i=ne[i])++tt,dfs3(to[i],rt);
        for(int i=1;i<=tt;i++){
            int x=v[i].size();if(!x)continue;
            q.push((node){v[i][x-1],x-1,i});
        }
        for(int i=1;i<=(k>>1);i++){
            node a=q.top();q.pop();
            node b=q.top();q.pop();
            printf("%d %d
    ",a.x,b.x);
            if(a.d>=1)q.push((node){v[a.id][a.d-1],a.d-1,a.id});
            if(b.d>=1)q.push((node){v[b.id][b.d-1],b.d-1,b.id});
        }
        return 0;
    }
    View Code
  • 相关阅读:
    C++拷贝构造函数(深拷贝,浅拷贝)
    C++标准库和标准模板库
    MFC 定制控件(Customize Control) 及 MFC CWnd和WIN32 HWND关联方法
    MFC 对象与Win32 SDK 句柄的映射关系
    关于函数返回值的几种情况
    CreateWaitableTimer和SetWaitableTimer函数
    关于C++/C中防止头文件的重复包含的解决办法
    MFC 分割窗体(Splitter Windows)
    MFC中对话框的数据交换(DDX)和数据校验(DDV)
    MFC 消息映射表 及 相关宏定义
  • 原文地址:https://www.cnblogs.com/Jessie-/p/10416047.html
Copyright © 2011-2022 走看看