来自学长的馈赠7

rand 2 分数255 忌骄,忌躁,这只是题目简单,不要被假象冲昏头

题纲:T1:图论,思维题

T2:板子,求最长不降序列

T3:线段树维护树上两点最值距离

T4:树上启发式合并(小的往大的里面并)+贪心策略

T3:给你一棵树,a,b,c,d,询问(ab)的点和(cd)点所组成的点对最远距离,多组询问

(1)结论:(ab)区间(A,B)是最远点对,(cd)区间(C,D)是,那么ab+cd的最远一定是(A,B)(A,D),(B,C),(B,D)中的,可以用树上某点到其他点的最远距离是直径一个端点解释,可合并区间,那就是线段树

(2)技巧:线段树传址调用,直接Query(&a,&b)一路算下a,b就直接是答案了

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 2;
const ll mod = 1e9 + 7;
const int INF = 2147483647;
const int lim = 1e4 + 1;

int n, siz[maxn], dep[maxn], fa[maxn];
int son[maxn], top[maxn], m, du[maxn];
ll ans, dis[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node 
{
    int next, to, w;
}a[maxn<<1];
int head[maxn], len;

void add(int x, int y, int w)
{
    a[++len].to = y; a[len].next = head[x]; a[len].w = w;
    head[x] = len;
}

void find_heavy_edge(int u, int fat, int depth)
{
    //printf("u = %d\n", u);
    fa[u] = fat;
    dep[u] = depth;
    siz[u] = 1;
    son[u] = 0;
    int maxsize = 0;

    for(int i=head[u]; i; i=a[i].next)
    {
        int v = a[i].to;
        if(dep[v]) continue;
        dis[v] = dis[u] + a[i].w;
        //printf("dis[%d] = %d\n", v, dis[v]);
        find_heavy_edge(v, u, depth+1);
        du[u]++;
        //dis[v] = dis[u] + a[i].w;
        //printf("dis[%d] = %d\n", v, dis[v]);
        siz[u] += siz[v];
        if(siz[v] > maxsize)
        {
            maxsize = siz[v];
            son[u] = v;
        }
    }
}

void connect_heavy_dege(int u, int ancestor)
{
    //dfn[u] = ++ntime;
    top[u] = ancestor;
    //rnk[ntime] = u;
    if(son[u])
    {
        connect_heavy_dege(son[u], ancestor);
    }
    for(int i=head[u]; i; i=a[i].next)
    {
        int v = a[i].to;
        if(v == son[u] || v == fa[u]) continue;
        connect_heavy_dege(v, v);
    }
}

int LCA(int x, int y)
{
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    return x;
}

ll get_dis(int x, int y)
{
    int lca = LCA(x, y);
    ll ans = dis[x]+dis[y]-2*dis[lca];
    return ans;
}

#define lson (rt<<1)
#define rson (rt<<1|1)

struct node2 
{
    int l, r, L, R;
}tree[maxn<<2];

void IWantToUseALongNameOnlyForHappiness(int a1, int a2, int b1, int b2, int &c1, int &c2)
{
    ll ans = 0;
    ll a = get_dis(a1, a2);
    ll b = get_dis(a1, b1);
    ll c = get_dis(a1, b2);
    ll d = get_dis(a2, b1);
    ll e = get_dis(a2, b2);
    ll f = get_dis(b1, b2);

    if(a > ans) ans = a, c1 = a1, c2 = a2;
    if(b > ans) ans = b, c1 = a1, c2 = b1;
    if(c > ans) ans = c, c1 = a1, c2 = b2;
    if(d > ans) ans = d, c1 = a2, c2 = b1;
    if(e > ans) ans = e, c1 = a2, c2 = b2;
    if(f > ans) ans = f, c1 = b1, c2 = b2;
}

void pushup(int rt)
{
    IWantToUseALongNameOnlyForHappiness(tree[lson].L, tree[lson].R, tree[rson].L, tree[rson].R, tree[rt].L, tree[rt].R);
}

void build(int rt, int l, int r)
{
    tree[rt].l = l; tree[rt].r = r;
    if(l == r)
    {
        tree[rt].L = tree[rt].R = l;
        return;
    }
    int mid = (l + r) >> 1;
    build(lson, l, mid);
    build(rson, mid+1, r);
    pushup(rt);
}

void query(int rt, int l, int r, int &L, int &R)
{
    //printf("rt = %d\n", rt);
    if(l <= tree[rt].l && tree[rt].r <= r)
    {
        IWantToUseALongNameOnlyForHappiness(L, R, tree[rt].L, tree[rt].R, L, R);
        return;
    }
    int mid = (tree[rt].l + tree[rt].r) >> 1;
    if(l <= mid) query(lson, l, r, L, R);
    if(r > mid) query(rson, l, r, L, R);
}

int main()
{
    n = read();
    for(int i=1; i<n; i++)
    {
        int x = read(), y = read(), w = read();
        add(x, y, w); add(y, x, w);
    }
    find_heavy_edge(1, 1, 1);
    connect_heavy_dege(1, 1);
    build(1, 1, n);
    //printf("111\n");
    m = read();
    while(m--)
    {
        int a = read(), b = read(), c = read(), d = read();
        int e = a, f = b, g = c, h = d;
        query(1, a, b, e, f);
        //printf("222\n");
        query(1, c, d, g, h);
        ans = max(get_dis(e, g), get_dis(e, h));
        ans = max(ans, max(get_dis(f, g), get_dis(f, h)));
        printf("%lld\n", ans);
    }
    
    return 0;
}

from Catherine_leah

T4:给你一棵树,要求把节点分成若干类,每类的权值就是这类点里面的点权最大的,要求每一类里的节点不能有祖先关系,求能构造出的若干类权值加和的最小值

(1)解法:从最简单的考虑,如果是一条1不是根的链,那么一定是从两条链里面每次选一个,取最大的作为类的权值,另一个一定跟在里面是最优的;类推到三个,就是1+1+1->2+1 2+1->3,类推到一颗子树,就是一样的,从叶子往上合并

(2)复杂度分析:在把rt的儿子依次合并的时候,必须启发式合并,小的往大的上合并,复杂度才是对的,可以用标号指向大根堆的下标,swap一下下标就可以,所以复杂度分析真的很重要!!!

int n,M[N],head[N],tot,du[N],tp[N],cn;
int id[N];
struct DUG
{
    int to,nxt;
}e[N<<1];
inline void Add(int x,int y)
{
    e[++tot].to=y,e[tot].nxt=head[x],head[x]=tot;
}
priority_queue<int>st[N];//最多n个节点
inline void Goback(int x,int fi)
{
    cn=0;
    if(st[id[x]].size()>st[id[fi]].size())swap(id[x],id[fi]);//让父亲是大的,id[x]是小的
   
        while(!st[id[x]].empty())
        {
            int xi=st[id[fi]].top(),yi=st[id[x]].top();
            tp[++cn]=max(xi,yi);
            st[id[fi]].pop(),st[id[x]].pop();
        }
        _f(i,1,cn)st[id[fi]].push(tp[i]);
    
}
inline void dfs(int x)
{
    for(rint i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        dfs(to);
        Goback(to,x);
    }
    st[id[x]].push(M[x]);//为了儿子不和父亲在一个内存条里出现,必须最后放进去
}
int main()
{
   //freopen("qiandao4.in","r",stdin);
   // freopen("dfs.txt","w",stdout);
    n=re();
    _f(i,1,n)M[i]=re(),id[i]=i;
    _f(i,2,n)
    {
        int x=re();Add(x,i);
    }
    dfs(1);
    ll ans=0;
    while(!st[id[1]].empty())
    {
        ans=ans+(ll)st[id[1]].top();
        st[id[1]].pop();
    }
    chu("%lld",ans);
    return 0;
}
posted on 2022-07-31 22:10  HZOI-曹蓉  阅读(17)  评论(0编辑  收藏  举报