LibreOJ #3052. 「十二省联考 2019」春节十二响 长链剖分

题意

给你n个点的树,你要构造出一些点集,使得每个点集里面的任何点对都不存在祖先关系,使得每个点集的最大值的和最小
n ≤ 2 ∗ 1 0 5 n\leq 2*10^5 n2105

分析

发现对于点集的数量其实和深度有关系
仔细分析,不同子树内的点集其实可以最大的和最大的消,次大的和次大的消,如果是祖先的话,就只能新开一个集合

这样的话用长链剖分就好了 拿轻链去跟重链消,每条重链只会贡献这条重链长度的点集,每条重链只有在轻边的时候进行统计,这样总共会被统计 n n n次,开个堆就行了

代码

#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#define pb push_back
#define mp make_pair
#define fi first
#define se second

using namespace __gnu_pbds;
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> splay;

const int N = 200010;
const int mod = 998244353;

inline int read()
{
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

struct node{int x,y,nex;}edge[N<<1]; int len,fir[N];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].nex = fir[x]; fir[x] = len;}

int n,m,a[N],son[N],dep[N];

void dfs(int x)
{
  dep[x] = 1;
  for(int k=fir[x];k!=-1;k=edge[k].nex){int y = edge[k].y; dfs(y); dep[x] = max(dep[y] + 1,dep[x]);}
  for(int k=fir[x];k!=-1;k=edge[k].nex){int y = edge[k].y; if(dep[y] + 1 == dep[x]) son[x] = y;}
}

priority_queue<int> q[N]; int id[N]; int sz = 0;
int tmp[N],tot;

void dfs2(int x)
{
  if(son[x])
  {
    dfs2(son[x]);
    id[x] = id[son[x]];
  }else id[x] = ++sz;
  for(int k=fir[x];k!=-1;k=edge[k].nex)
  {
    int y = edge[k].y;
    if(y==son[x]) continue; dfs2(y);
    tot = 0; while(q[id[y]].size())
    {
      tmp[++tot] = max(q[id[y]].top() , q[id[x]].top());
      q[id[y]].pop(); q[id[x]].pop();
    }while(tot){q[id[x]].push(tmp[tot]); tot--;}
  }
//  printf("%d %d\n",x,q[id[x]].size());
  q[id[x]].push(a[x]);
}

int main()
{
  len = 0; memset(fir,-1,sizeof(fir));
  n = read(); for(int i=1;i<=n;i++) a[i] = read();
  for(int i=2;i<=n;i++) ins(read(),i);
  dfs(1);
  sz = 0; dfs2(1);
  // for(int i=1;i<=n;i++) printf("%d ",id[i]); printf("\n");
  ll s = 0;  while(!q[id[1]].empty()) s+=q[id[1]].top(),q[id[1]].pop();
  return printf("%lld\n",s),0; 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值