【Codeforces 1213G】Path Queries(树分治)

题目链接

正解应该是按权值从小到大加入边,计算合并联通块的贡献。不过看到这题我第一眼想到的是树分治…很久没写过了正好练习一下。

记录 a n s [ x ] ans[x] ans[x]为路径上权值最大值恰好为 x x x的路径条数。每层分治时记录每个点 x x x到根的最大值 P [ x ] P[x] P[x],先把各点到根路径处理完,剩下的就是跨越根的路径。对于不同子树内的两点 x , y x,y x,y,路径 ( x , y ) (x,y) (x,y)的上的权值最大值是 m a x ( P [ x ] , P [ y ] ) max(P[x],P[y]) max(P[x],P[y])。我们先简单地用计数器处理掉 P [ x ] = P [ y ] P[x]=P[y] P[x]=P[y]的情况,然后对于每个点 x x x,统计与其不在同一子树内的点中,满足 P [ y ] < P [ x ] P[y]<P[x] P[y]<P[x]的点数 S S S,则 x x x对于 a n s [ P [ x ] ] ans[P[x]] ans[P[x]]的贡献就是 S S S。我没有多想,直接拿了个树状数组,先把所有 P [ y ] P[y] P[y]计数器加一,处理一个子树时把子树内的 P [ y ] P[y] P[y]计数器减一,对每个 x x x询问 [ 1 , P [ x ] − 1 ] [1,P[x]-1] [1,P[x]1]内的计数器之和,子树处理后再复原。回答询问时用 a n s ans ans的前缀和即可。复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n),不知道树状数组那个地方的 l o g log log可不可以去掉。【感觉整个代码写得好累赘啊qwq。】

 

P.S. 中间犯了一个蠢错误,就是对子树一边处理一边复原…当时天真地以为把复原放在DFS的末尾,就能使每个点处理时,整个子树的信息还没复原,但这显然不对,树如果分叉了,先处理右支,再处理左支时右支的信息已经还原了qwq。

 

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<string.h>
#include<set>
#define LL long long
using namespace std;
LL read( )
{
  LL sum=0;char c=getchar( );bool f=0;
  while(c<'0' || c>'9') {if(c=='-') f=1;c=getchar( );}
  while(c>='0' && c<='9') {sum=sum*10+c-'0';c=getchar( );}
  if(f) return -sum;
  return sum;
}
const int N=200005;
struct ex{int num,next,w;}map[N*2];
int head[N],len;
void link(int x,int y,int w)
{
  len++;map[len].num=y;map[len].next=head[x];head[x]=len;map[len].w=w;
  len++;map[len].num=x;map[len].next=head[y];head[y]=len;map[len].w=w;
}
int n,q,mw;LL ans[N];
//Core
int tot,G,mx,sz[N];bool del[N];
void rex(int k,int f)
{
  int i,x;
  sz[k]=1;tot++;
  for(i=head[k];i;i=map[i].next)
    {
      x=map[i].num;
      if(x==f||del[x]) continue;
      rex(x,k);
    }
}
void dfs(int k,int f)
{
  int i,x,y=0;
  for(i=head[k];i;i=map[i].next)
    {
      x=map[i].num;
      if(x==f||del[x]) continue;
      dfs(x,k);sz[k]+=sz[x];
      y=max(y,sz[x]);
    }
  y=max(y,tot-sz[k]);
  if(y<mx) mx=y,G=k;
}
//Calculate
int cnt=0,tim[N],cal[N],a[N];
void add(int x) {for(int i=x;i<=mw;i+=i&-i) a[i]++;}
void sub(int x) {for(int i=x;i<=mw;i+=i&-i) a[i]--;}
int ask(int x) {int i,r=0;for(i=x;i;i-=i&-i) r+=a[i];return r;}
void dfs1(int k,int f,int MX)
{
  if(tim[MX]!=cnt) tim[MX]=cnt,cal[MX]=0;
  ans[MX]+=cal[MX];ans[MX]++;
  int i,x,y;
  for(i=head[k];i;i=map[i].next)
    {
      x=map[i].num;
      if(x==f||del[x]) continue;
      dfs1(x,k,max(MX,map[i].w));
    }
}
void dfs2(int k,int f,int MX)
{
  cal[MX]++;add(MX);
  int i,x,y;
  for(i=head[k];i;i=map[i].next)
    {
      x=map[i].num;
      if(x==f||del[x]) continue;
      dfs2(x,k,max(MX,map[i].w));
    }
}
void dfs3(int k,int f,int MX)
{
  ans[MX]+=ask(MX-1);
  int i,x,y;
  for(i=head[k];i;i=map[i].next)
    {
      x=map[i].num;
      if(x==f||del[x]) continue;
      dfs3(x,k,max(MX,map[i].w));
    }
}
void dfs4(int k,int f,int MX)
{
  sub(MX);
  int i,x,y;
  for(i=head[k];i;i=map[i].next)
    {
      x=map[i].num;
      if(x==f||del[x]) continue;
      dfs4(x,k,max(MX,map[i].w));
    }
}
void dfs5(int k,int f,int MX)
{
  add(MX);
  int i,x,y;
  for(i=head[k];i;i=map[i].next)
    {
      x=map[i].num;
      if(x==f||del[x]) continue;
      dfs5(x,k,max(MX,map[i].w));
    }
}
void solve(int k)
{
  cnt++;tot=0;rex(k,0);
  if(tot==1) return;
  mx=n;dfs(k,0);
  int i,x;
  for(i=head[G];i;i=map[i].next)
    {
      x=map[i].num;
      if(del[x]) continue;
      dfs1(x,G,map[i].w);
      dfs2(x,G,map[i].w);
    }
  for(i=head[G];i;i=map[i].next)
    {
      x=map[i].num;
      if(del[x]) continue;
      dfs4(x,G,map[i].w);
      dfs3(x,G,map[i].w);
      dfs5(x,G,map[i].w);
    }
  for(i=head[G];i;i=map[i].next)
    {
      x=map[i].num;
      if(del[x]) continue;
      dfs4(x,G,map[i].w);
    }
  del[G]=1;
  for(i=head[G];i;i=map[i].next)
    {
      x=map[i].num;
      if(del[x]) continue;
      solve(x);
    }
}
int main( )
{
  int i,j,x,y,z;
  n=read( );q=read( );
  for(i=1;i<n;i++)
    x=read( ),y=read( ),z=read( ),link(x,y,z),mw=max(z,mw);
  solve(1);
  for(i=2;i<=mw;i++) ans[i]+=ans[i-1];
  while(q--) printf("%lld ",ans[min(read( ),1LL*mw)]);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值