【树链剖分】【树状数组】【最近公共祖先】【块状树】bzoj3631 [JLOI2014]松鼠的新家

裸题,树状数组区间修改+单点查询。当然要稍微讨论一下链的左右端点是否修改的情况咯。

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 300001
int en,v[N<<1],first[N],next[N<<1],n;
void AddEdge(const int &U,const int &V)
{
	v[++en]=V;
	next[en]=first[U];
	first[U]=en;
}
int dep[N],siz[N],fa[N],son[N],top[N],Num[N],tot;
void dfs(int U,int d)
{
    dep[U]=d;
    siz[U]=1;
    for(int i=first[U];i;i=next[i])
      if(v[i]!=fa[U])
        {
          fa[v[i]]=U;
          dfs(v[i],d+1);
          siz[U]+=siz[v[i]];
          if(siz[v[i]]>siz[son[U]])
            son[U]=v[i];
        }
}
void dfs2(int U)
{
    if(son[U])
      {
        top[son[U]]=top[U];
        Num[son[U]]=++tot;
        dfs2(son[U]);
      }
    for(int i=first[U];i;i=next[i])
      if(v[i]!=fa[U]&&v[i]!=son[U])
        {
          top[v[i]]=v[i];
          Num[v[i]]=++tot;
          dfs2(v[i]);
        }
}
int SIZ[N],TOP[N],sz;
void dfs3(int U)
{
    for(int i=first[U];i;i=next[i])
      if(v[i]!=fa[U])
        {
          if(SIZ[TOP[U]]<sz)
            {
              TOP[v[i]]=TOP[U];
              ++SIZ[TOP[U]];
            }
          dfs3(v[i]);
        }
}
int lca(int U,int V)
{
    while(U!=V)
      {
        if(TOP[U]!=TOP[V])
          {
            if(dep[TOP[U]]<dep[TOP[V]])
              swap(U,V);
            U=fa[TOP[U]];
          }
        else
          {
            if(dep[U]<dep[V])
              swap(U,V);
            U=fa[U];
          }
      }
    return U;
}
int d[N];
void add_node(int p,const int &v){for(;p<=n;p+=(p&(-p))) d[p]+=v;}
void add_range(const int &L,const int &R){add_node(L,1);if(R!=n)add_node(R+1,-1);}
int query(int p){int res=0;for(;p;p-=(p&(-p)))res+=d[p];return res;}
void update(int U,int V)
{
	int f1=top[U],f2=top[V];
	while(f1!=f2)
	  {
	  	if(dep[f1]<dep[f2])
	  	  {
	  	  	swap(f1,f2);
	  	  	swap(U,V);
	  	  }
	  	add_range(Num[f1],Num[U]);
	  	U=fa[f1];
	  	f1=top[U];
	  }
	if(dep[U]>dep[V])
	  swap(U,V);
	add_range(Num[U],Num[V]);
}
int cnt,LL[N],RR[N];
void dfs4(int U)
{
	LL[U]=++cnt;
	for(int i=first[U];i;i=next[i])
	  if(v[i]!=fa[U])
	    dfs4(v[i]);
	RR[U]=cnt;
}
int xu[N];
void work(const int &L,const int &R,const int &op)
{
	if(op==n-1)
	  {
	  	if(fa[L]==R||fa[R]==L) return;
	  	int LCA=lca(L,R);
	  	if(LCA==L)
	  	  {
	  	  	for(int i=first[L];i;i=next[i])
	  	  	  if(v[i]!=fa[L]&&LL[R]>=LL[v[i]]&&LL[R]<=RR[v[i]])
	  	    	{
	  	    	  update(v[i],fa[R]);
	  	      	  return;
	  	    	}
	  	  }
	  	else if(LCA==R)
	  	  {
	  	  	for(int i=first[R];i;i=next[i])
	  	  	  if(v[i]!=fa[R]&&LL[L]>=LL[v[i]]&&LL[L]<=RR[v[i]])
	  	    	{
	  	    	  update(fa[L],v[i]);
	  	      	  return;
	  	    	}
	  	  }
	  	else update(fa[L],fa[R]);
	  	return;
	  }
	if(lca(L,R)==L)
	  {
	  	for(int i=first[L];i;i=next[i])
	  	  if(v[i]!=fa[L]&&LL[R]>=LL[v[i]]&&LL[R]<=RR[v[i]])
	  	    {
	  	      update(v[i],R);
	  	      return;
	  	    }
	  }
	else update(fa[L],R);
}
int main()
{
	int A,B;
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&xu[i]);
	for(int i=1;i<n;++i)
	  {
	  	scanf("%d%d",&A,&B);
	  	AddEdge(A,B);
	  	AddEdge(B,A);
	  }
	top[1]=1;
	Num[1]=++tot;
	dfs(1,1);
	dfs2(1);
	sz=sqrt(n); if(!sz) sz=1;
    for(int i=1;i<=n;i++)
      {
        SIZ[i]=1;
        TOP[i]=i;
      }
    dfs3(1);
	dfs4(1);
	if(n==2)
	  {
	  	work(xu[2],xu[1],0);
	  	goto OUT;
	  }
	update(xu[1],xu[2]);
	for(int i=2;i<n;++i)
	  work(xu[i],xu[i+1],i);
	OUT:for(int i=1;i<=n;++i)
	  printf("%d\n",query(Num[i]));
	return 0;
}
时间: 2024-10-29 21:01:07

【树链剖分】【树状数组】【最近公共祖先】【块状树】bzoj3631 [JLOI2014]松鼠的新家的相关文章

BZOJ3631: [JLOI2014]松鼠的新家 树链剖分/lca/树上查分

求n次lca后树上差分. 每次移动时在起始点和终点各打一个start标记,在LCA和LCA的父节点处各上打一个end标记,然后深搜,start标记一直上传,遇到end标记就停止,最后再处理一下就行 % PoPoQQQ大爷 #include<bits/stdc++.h> #define rep(i,l,r) for(int i=l;i<=r;i++) #define N 310000 using namespace std; int head[N],dep[N],sz[N],son[N],

BZOJ 3631: [JLOI2014]松鼠的新家( 树链剖分 )

裸树链剖分... ------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; const int maxn = 300009; struct edge { int to; edge* next; } E[maxn << 1], *pit = E, *head[maxn]; inline void add(int u,

[JLOI2014]松鼠的新家 (树剖)

题目 P3258 [JLOI2014]松鼠的新家 解析 非常裸的一道树剖题 链上修改+单点查询的板子 记录一下所经过的点\(now[i]\),每次更新\(now[i-1]到now[i]\) 我们链上更新时上一次到的终点,是这一次一次更新的起点,又因为在\(a_n\)处可以不放糖,所以我们每次链上更新完成后,在这条链的终点位置处糖数\(-1\). 然后套板子直接做 代码 #include <bits/stdc++.h> using namespace std; const int N = 2e6

【树链剖分】【dfs序】【线段树】bzoj2836 魔法树

这道题告诉我们:树链剖分的重标号就是dfs序. #include<cstdio> #include<algorithm> using namespace std; #define N 100001 #define lson rt<<1,l,m #define rson rt<<1|1,m+1,r typedef long long ll; ll delta[N<<2],sumv[N<<2]; int n,m; int en,v[N],

BZOJ 3631 JLOI2014 松鼠的新家 树链剖分/LCA

题目大意:给定一棵无根树和一个序列,在这个序列上依次遍历,求每个点的访问次数(最后一个点的访问次数要-1) 树链剖分的裸题--考场上我还是一个弱渣,啥也不会,暴力得了50分,剩下两道题爆零了...而且30W深搜爆栈,人生第一次手写了系统栈.. 回来因为这题的原因去学了树链剖分 结果没学明白 每条重链单独开了一棵线段树 常数大的要死 高一时写的代码...还是别看了,拿去对拍可以,阅读性欠佳 #include<stdio.h> #include<stdlib.h> #include&l

树链剖分模板题(luogu3384 【模板】树链剖分)

题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数N.M.R.P,分别表示树的结点个数.操作个数

[JLOI2014]松鼠的新家-树链剖分

最开始的时候我在写线段树部分的时候还打了一个build,后来一想,打个球球大作战的build啊!!!有个锤子的用啊!!! 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn = 3e6+5; 4 int n; 5 int e,begin[maxn],next[maxn],to[maxn],a[maxn]; 6 int tree[maxn<<2],lazy[maxn<<2]; 7 int so

HDU 5044 Tree 树链剖分

一棵树,初始边权和点权都为0 现在有m个操作,每一个操作: ADD1 u v k: for nodes on the path from u to v, the value of these nodes increase by k. ADD2 u v k: for edges on the path from u to v, the value of these edges increase by k. 操作完后,输出每一个点的点权和每一条边的边权(边权按照输入顺序输出) 我们把边权也当做点权处

[总结]树链剖分的详细介绍

目录 一.关于树链剖分 二.树链剖分实现流程 二.树链剖分具体实现 1.需要表示的变量 2.储存一棵树 3.第一次遍历,处理fa,dep,size,son数组 4.第二次遍历,处理top,seg,rev数组 5.初始化线段树 6.单点修改 7.区间修改---以x为根结点的子树内节点的值都加val 8.区间修改---节点x到节点y的最短路径中同时加val 9.区间查询---以x为根结点的子树内节点的值的和 10.区间查询---节点x到节点y的最短路径中节点的和 11.区间查询---节点x到节点y的