题目:[Tree]树中的配对(set&思维)

题目

传送门

思路

首先我们解决第一个问题

怎么使值最大?

这个可以通过公式变形来解决

\(ans=\sum_{i=1}^{n}(dep_i+dep_{p_i}-2*dep_{lca_{i,p_i}})=2*\sum_{i=1}^{n}dep_i-2*\sum_{i=1}^{n}dep_{lca_{i,p_i}}\)

下面我们看看这能得到什么

p序列是客观存在的,这是与哪个节点是根节点是没有关系的

所以对于一个固定的根节点,他的最大值也是答案

然而我们已知,对于一个根节点,式子的前半部分的值是固定的,所以我们使后一段尽可能的小,当后一段最小时,这就是答案,并且这个答案就是最大值

为了方便,我们就将根结点定为重心,后半部分很容依旧能得到0

第一部分就解决了

下面来说第二部分

对于编号,我们直接找最小的编号就行了

但是要注意一个情况,

我们将一个点拆成一个入点和出点

就是我们将连接看成单项的

我们定义\(in_u\text{和}out_u\)表示第u个子树还没有匹配的出点,和还没有匹配的入点数量

我们考虑我们是怎样将使最后一段等于0的,也就是选的两个点必须不在同一颗子树

也就是对于任何一颗子树我们必须满足

\(in_u+out_u\le\sum_{i=1}^{i\in 子树}(in_i+out_i)-in_u-out_u\)

也就是说,当某一颗子树\(i\)使这个等式取等号的时候,我们对于当前点,

如果当前点不在这颗最大的子树内,这个点必须与子树\(i\)中的某一个节点匹配

对于\(in\)和\(out\)的维护用线段树维护,

最后我们需要将根特殊考虑,为什么?

因为根可以和任意节点匹配,包括自己

代码

#include<iostream>
#include<vector>
#include<set>
using namespace std;
struct node1
{
    int e;
    long long w;
};
struct node_set
{
    int e;
    int w;
    friend bool operator < (const node_set &a,const node_set &b)
    {
        return a.w<b.w;
    }
};
struct node_tre
{
    int l;
    int r;
    int maxx;
    int s1;//未匹配的入点
    int s2;//未匹配的出点

}tre[400005];
int n;
int rt;
int siz[100005];
int bel[100005];
long long dep[100005];
vector<node1> g[100005];
long long ans;
set<int> in[100005];
set<node_set> minn;
void build(int l,int r,int k)
{
    tre[k].l=l;
    tre[k].r=r;
    tre[k].maxx=tre[k].s1=tre[k].s2=0;
    if(l==r)
    {
        tre[k].maxx=in[l].size()*2;
        tre[k].s1=in[l].size();
        tre[k].s2=in[l].size();
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
    tre[k].maxx=max(tre[k<<1].maxx,tre[k<<1|1].maxx);
    tre[k].s1=tre[k<<1].s1+tre[k<<1|1].s1;
    tre[k].s2=tre[k<<1].s2+tre[k<<1|1].s2;
}
void delet1(int u,int k)//减去入点
{
    if(tre[k].l>u||tre[k].r<u)
        return ;
    if(tre[k].l==tre[k].r)
    {
        tre[k].s1--;
        tre[k].maxx=tre[k].s1+tre[k].s2;
        return;
    }
    delet1(u,k<<1);
    delet1(u,k<<1|1);
    tre[k].maxx=max(tre[k<<1].maxx,tre[k<<1|1].maxx);
    tre[k].s1=tre[k<<1].s1+tre[k<<1|1].s1;
    tre[k].s2=tre[k<<1].s2+tre[k<<1|1].s2;
}
void delet2(int u,int k)//减去出点
{
    if(tre[k].l>u||tre[k].r<u)
        return ;
    if(tre[k].l==tre[k].r)
    {
        tre[k].s2--;
        tre[k].maxx=tre[k].s1+tre[k].s2;
        return;
    }
    delet2(u,k<<1);
    delet2(u,k<<1|1);
    tre[k].maxx=max(tre[k<<1].maxx,tre[k<<1|1].maxx);
    tre[k].s1=tre[k<<1].s1+tre[k<<1|1].s1;
    tre[k].s2=tre[k<<1].s2+tre[k<<1|1].s2;
}
int ask(int k,int fin)
{
    if(tre[k].maxx!=fin)
        return 0;
    if(tre[k].l==tre[k].r)
        return tre[k].l;
    int t=ask(k<<1,fin);
    if(t)
        return t;
    return ask(k<<1|1,fin);
}
void dfs_fouc(int u,int fa)
{
    int maxx=-1;
    siz[u]=1;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i].e;
        if(v!=fa)
        {
            dfs_fouc(v,u);
            siz[u]+=siz[v];
            maxx=max(maxx,siz[v]);
        }
    }
    maxx=max(maxx,n-siz[u]);
    if(n/2>=maxx)
        rt=u;
}
void dfs_dep(int u,int fa)
{
    ans+=dep[u]*2;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i].e;
        if(v!=fa)
        {
            dep[v]=dep[u]+g[u][i].w;
            dfs_dep(v,u);
        }
    }
}
void dfs_bel(int u,int fa,int _ind)
{
    bel[u]=_ind;
    in[_ind].insert(u);
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i].e;
        if(v!=fa)
            dfs_bel(v,u,_ind);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int u,v;
        long long w;
        cin>>u>>v>>w;
        g[u].push_back((node1){v,w});
        g[v].push_back((node1){u,w});
    }
    dfs_fouc(1,0);
    dfs_dep(rt,0);
    for(int i=0;i<g[rt].size();i++)
        dfs_bel(g[rt][i].e,rt,i+1);
    bel[rt]=g[rt].size()+1;
    in[g[rt].size()+1].insert(rt);
    for(int i=1;i<=g[rt].size()+1;i++)
    {
        set<int>::iterator it=in[i].begin();
        minn.insert((node_set){i,*it});
    }
    cout<<ans<<'\n';
    build(1,g[rt].size()+1,1);
    for(int i=1;i<=n;i++)
    {
        int u=ask(1,tre[1].maxx);
        if(tre[1].maxx>=n-i+1&&(bel[i]!=u&&u!=g[rt].size()+1))
        {
            set<int>::iterator it=in[u].begin();
            int t=*it;
            cout<<t<<' ';
            in[u].erase(in[u].find(t));
            minn.erase(minn.find((node_set){u,t}));
            delet1(u,1);
            delet2(bel[i],1);
            if(!in[u].empty())
            {
                it=in[u].begin();
                minn.insert((node_set){u,*it});
            }
        }
        else
        {
            set<node_set>::iterator it1=minn.begin();
            node_set t=*it1;
            if(t.e==bel[i]&&t.w!=rt)
            {
                minn.erase(it1);
                set<node_set>::iterator it2=minn.begin();
                node_set t2=*it2;
                minn.insert(t);
                t=t2;
            }
            cout<<t.w<<' ';
            delet1(t.e,1);
            delet2(bel[i],1);
            minn.erase(minn.find(t));
            in[t.e].erase(in[t.e].find(t.w));
            if(!in[t.e].empty())
            {
                set<int>::iterator it=in[t.e].begin();
                minn.insert((node_set){t.e,*it});
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/loney-s/p/12219353.html

时间: 2025-01-18 02:16:34

题目:[Tree]树中的配对(set&思维)的相关文章

hihocoder 挑战赛9 A.好配对(思维题目 防止超时)

#1123 : 好配对 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 给定两个序列a和b,每个序列中可能含有重复的数字. 一个配对(i,j)是一个好配对当从第一个序列中选出一个数ai,再从第二个序列中选出一个数bj且满足ai>bj. 给出两个序列,问存在多少个好配对. 输入 输入包含多组数据,数据第一行一个整数T,表示数据组数.(T<=5) 每组数据第一行包含两个整数n和m.(0<n,m<=105) 接下来n行,每行两个整数x和y,表示在第一个序列中有

Lowest Common Ancestor of a Binary Search Tree(树中两个结点的最低公共祖先)

题目描述: Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST. According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T tha

[LeetCode] 834. Sum of Distances in Tree 树中距离之和

An undirected, connected?tree with?N?nodes labelled?0...N-1?and?N-1?edges?are?given. The?ith edge connects nodes?edges[i][0]?and?edges[i][1]?together. Return a list?ans, where?ans[i]?is the sum of the distances between node?i?and all other nodes. Exa

codeforces 1029E Tree with Small Distances【思维+贪心】 【非原创】

题目:戳这里 学习博客:戳这里 题意:给一个树加最少的边,使得1到所有点的距离小于等于2. 解题思路:分析样例3可以看出,如果一个点到1的距离大于2,那么建立1到该点的父亲节点的边将比直接与该点建边更优.官方题解的做法是把所有与1距离大于2的点放到大顶堆里维护,每次取出最远的点,染色与该点父节点以及与父节点相接的所有点.也就是相当于与父节点建边,距离为1,与父节点的子节点的距离就为2了,于是把这些点弹出.从大佬博客中学到一个很妙的写法,是用dfs实现这个思路,具体看代码. 附ac代码: 1 #i

AtCoder Beginner Contest 116 C题 【题意:可以在任意区间【L,R】上加1,求通过最少加1次数得到题目给定的区间】】{思维好题}

C - Grand Garden In a flower bed, there are NN flowers, numbered 1,2,......,N1,2,......,N. Initially, the heights of all flowers are 00. You are given a sequence h={h1,h2,h3,......}h={h1,h2,h3,......} as input. You would like to change the height of

[51nod][cf468D]1558 树中的配对

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1558 不是很懂dalao们用线段树是怎么写的-- 反正找出重心以后每个子树一个堆,再来个全局堆就吼了 #include<queue> #include<stdio.h> #include<algorithm> #define MN 100001 using namespace std; int read_p,read_ca; inline in

求解性思维与验证性思维

题目分析: Given a list of unique words. Find all pairs of distinct indices (i, j) in the given list, so that the concatenation of the two words, i.e. words[i] + words[j] is a palindrome. Example 1: Given words = ["bat", "tab", "cat&qu

[LeetCode] Reverse Linked List II @ Python

原题地址:https://oj.leetcode.com/problems/reverse-linked-list-ii/ 题意: Reverse a linked list from position m to n. Do it in-place and in one-pass. For example:Given 1->2->3->4->5->NULL, m = 2 and n = 4, return 1->4->3->2->5->NULL.

网络流24题刷题记录

题目一:飞行员配对方案问题 一群飞行员和另一群飞行员之间有的可以配对,有的不能配对,求最多可以配多少对? 典型二分图最大匹配问题.可以用匈牙利算法 或者 加上源点汇点之后跑最大流.[感觉第二个打错的概率还低一些]. [还是介绍一下匈牙利算法吧][看白书大法好!] 从左边(s集)一个未盖点出发(还有没有和任何人匹配的点)出发,顺次经过未选边->选边->未选边.....[这样的路叫做交替路] 如果路径当中经过一个未盖点[这样的交替路叫增广路]...那么将所有的选边变成不选,不选的边选上,就可以多一