【树上莫队】bzoj3757 苹果树

学习这位神犇的:http://blog.csdn.net/jiangyuze831/article/details/41476865

注意:

①排序时第一关键字是左端点所在块编号(块状树),第二关键字是右端点dfs序。

②维护的当前链不能包括lca(l,r),但最后要计算上lca(l,r)的答案。

③对l->l‘/r->r‘取反的时候也不能取反lca(l,l‘)/lca(r,r‘)。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define N 50001
#define M 100001
int n,m;
int first[N],en,next[N<<1],dfsn[N],v[N<<1],fa[N],bel[N],dep[N],siz[N],cnt,top[N];
int x,y,root,sz,col[N],Time[N],ans,anss[M];
bool vis[N];
struct ASK{int l,r,a,b,id;void Read(){scanf("%d%d%d%d",&l,&r,&a,&b);}}Q[M];
bool operator < (const ASK &a,const ASK &b)
{return bel[a.l]!=bel[b.l] ? bel[a.l]<bel[b.l] : dfsn[a.r]<dfsn[b.r];}
void AddEdge(const int &U,const int &V)
{
	v[++en]=V;
	next[en]=first[U];
	first[U]=en;
}
void dfs(int cur)
{
	dfsn[cur]=++cnt;
	for(int i=first[cur];i;i=next[i])
	  if(v[i]!=fa[cur])
	    {
	      dep[v[i]]=dep[cur]+1;
          fa[v[i]]=cur;
          dfs(v[i]);
	    }
}
void makeblock(int cur)
{
    for(int i=first[cur];i;i=next[i])
      if(v[i]!=fa[cur])
        {
          if(siz[bel[cur]]<sz)
            {
              ++siz[bel[cur]];
              bel[v[i]]=bel[cur];
              top[v[i]]=top[cur];
            }
          makeblock(v[i]);
        }
}
int QLCA(int U,int V)
{
    while(U!=V)
      {
        if(top[U]==top[V])
          {
            if(dep[U]<dep[V])
			  swap(U,V);
            U=fa[U];
          }
        else
          {
            if(dep[top[U]]<dep[top[V]])
			  swap(U,V);
            U=fa[top[U]];
          }
      }
    return U;
}
void update(const int &Col,const int &op)
{
	Time[Col]+=op;
	if(!Time[Col]) --ans;
	else if(Time[Col]==1&&op==1) ++ans;
}
void Work(int U,int V,int lca)
{
	while(U!=lca)
	  {
        vis[U]^=1;
	  	update(col[U],vis[U]?1:-1);
        U=fa[U];
	  }
	while(V!=lca)
	  {
        vis[V]^=1;
	  	update(col[V],vis[V]?1:-1);
        V=fa[V];
	  }
}
void Solve(const int &p)
{
    int lca=QLCA(Q[p].l,Q[p].r);
    if(p==1) Work(Q[p].l,Q[p].r,lca);
    else
      {
      	Work(Q[p-1].l,Q[p].l,QLCA(Q[p-1].l,Q[p].l));
      	Work(Q[p-1].r,Q[p].r,QLCA(Q[p-1].r,Q[p].r));
      }
    update(col[lca],1);
    anss[Q[p].id]=ans;
    if(Q[p].a!=Q[p].b)
      anss[Q[p].id]-=(Time[Q[p].a]&&Time[Q[p].b]);
    update(col[lca],-1);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&col[i]);
	for(int i=1;i<=n;++i)
	  {
	  	scanf("%d%d",&x,&y);
	  	if(!x) root=y;
	  	else if(!y) root=x;
	  	else
	  	  {
	  	  	AddEdge(x,y);
	  	  	AddEdge(y,x);
	  	  }
	  }
	dfs(root);
	for(int i=1;i<=n;i++)
	  {
	  	siz[bel[i]=dfsn[i]]=1;
	    top[i]=i;
	  }
	sz=(((int)sqrt(n))<<1);
	makeblock(root);
	for(int i=1;i<=m;++i) Q[i].Read(),Q[i].id=i;
	sort(Q+1,Q+m+1);
	for(int i=1;i<=m;++i) Solve(i);
	for(int i=1;i<=m;++i) printf("%d\n",anss[i]);
	return 0;
}
时间: 2024-10-16 14:35:25

【树上莫队】bzoj3757 苹果树的相关文章

【BZOJ-3757】苹果树 块状树 + 树上莫队

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1305  Solved: 503[Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个到n之间的正整数来表示一种颜色.树上

bzoj 3757: 苹果树(树上莫队)

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 1327  Solved: 510 [Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个1到n之间的正整数来表示一种颜色

【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)

学习了树上莫队,树分块后对讯问的$dfs序$排序,然后就可以滑动树链处理答案了. 关于树链的滑动,只需要特殊处理一下$LCA$就行了. 在这里一条树链保留下来给后面的链来转移的$now$的为这条树链上所有点除去$LCA$的颜色种数.因为如果要考虑$LCA$情况就太多了,不如单独考虑$LCA$. 转移后加上当前链的$LCA$进行统计,然后再去掉这个$LCA$更新一下$now$值给后面的链转移. 这都是我的理解,说的有点不清楚,具体请看vfk的题解 OTZ 虽然不是这道题,但是通过这篇博客学习树上莫

BZOJ 3757 苹果树 树上莫队

题目大意:给出一棵树,问任意两点之间有多少种不同的颜色,一个人可能会有色盲,会将A和B当成一种颜色. 思路:比较裸的树上莫队,写出来之后,很慢,怀疑是分块的缘故,然后果断找了当年比赛的标称交上去,瞬间rk1,大概看了一眼,他好像是直接用DFS序+曼哈顿距离最小生成树搞的,为什么会比分块快? 昨天下午看到这个题之后就一直在研究树上莫队的正确姿势,然后先写了树分块,后来看了很多牛人的SPOJ COT2的题解,后来又和同学探讨了好久才弄明白. 首先先将树分块,然后把区间排序,按照第一权值为左端点所在块

(树上莫队)HDU - 5799 This world need more Zhu

题意: 两种询问: 1.询问以u为根的子树中出现的a次的数的和与出现b次的数的和的gcd. 2.询问u到v的树链中出现的a次的数的和与出现b次的数的和的gcd. 有点绕.. 分析: 因为自己不会树上莫队,所以学习了一波. 但是对于子树我还是有所经验,可以转成dfs序来做,之前有做过类似的题,比如这题. 然而对于树链有点懵逼,虽然我觉得也能用dfs序做,不过看大佬们的dfs序做的树链查询,也有点懵,感觉写起来很麻烦. 貌似是修改了dfs序,回溯的时候不再只是和进入时相同的序,而是独立的序. 还是感

[spoj COT2]树上莫队

题目链接:http://www.spoj.com/problems/COT2/ 学会了树上莫队,真的是太激动了!参照博客:http://codeforces.com/blog/entry/43230 讲的十分清楚. #include<bits/stdc++.h> using namespace std; const int MAXN=40005; const int maxm=100005; int cur; int sat[MAXN]; int ean[MAXN]; int A[MAXN*2

SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)

题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无意中看到树上莫队,只是拿来练练,没有想到这题的难点不在于树上莫队,而是判断LCA是否在两点之间的路径上的问题.耗时1天. 树上莫队的搞法就是: (1)DFS一次,对树进行分块,分成sqrt(n)块,每个点属于一个块.并记录每个点的DFS序. (2)将m个询问区间用所属块号作为第一关键字,DFS序作为第二关键字

[BZOJ 3052] [wc2013] 糖果公园 【树上莫队】

题目链接:BZOJ - 3052 题目分析 这道题就是非常经典的树上莫队了,并且是带修改的莫队. 带修改的莫队:将询问按照 左端点所在的块编号为第一关键字,右端点所在的块为第二关键字,位于第几次修改之后为第三关键字 排序. 我们将块的大小设置在 n^(2/3) ,这样一共有 n^(1/3) 个块.最后算法的总复杂度会是 n^(5/3) . 每一次如何从上一个询问转移来呢? 假设上一个询问是 (lx, ly, lt) ,这次的询问是 (x, y, t) .t 代表是在第 t 次修改操作之后. 首先

BZOJ3052 [wc2013] 糖果公园 【树上莫队】

树上莫队和普通的序列莫队很像,我们把树进行dfs,然后存一个长度为2n的括号序列,就是一个点进去当作左括号,出来当作右括号,然后如果访问从u到v路径,我们可以转化成括号序列的区间,记录x进去的时候编号为f[x],出来时为g[x],然后分类讨论一下(f[u]<f[v]),如果u和v的lca不是u,那么就是从g[u]到f[v],否则就是lca的f到另一个点的f,(可以自己试一下,中间过程没有用的点正好就抵消掉了)这里要注意一下,从g[u]到f[v]的时候我们会少掉lca这个点,特殊处理一下即可,然后

【WC2013】 糖果公园 - 树上莫队

[问题描述] Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩.糖果公园的结构十分奇特,它由 n 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 1 至 n.有 n – 1 条 双向道路 连接着这些游览点,并且整个糖果公园都是 连通的 ,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点.糖果公园所发放的糖果种类非常丰富,总共有 m 种,它们的编号依次为 1至 m.