【树上莫队】【带修莫队】【权值分块】bzoj4129 Haruna’s Breakfast

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 50001
int v[N<<1],en,first[N],next[N<<1];
void AddEdge(int U,int V)
{
    v[++en]=V;
    next[en]=first[U];
    first[U]=en;
}
int n,m,a[N],b[N];
int eq,ec,blo,sz,siz[N],top[N],fa[N],dep[N],num[N];
void dfs(int U)
{
    for(int i=first[U];i;i=next[i])
      if(v[i]!=fa[U])
        {
          fa[v[i]]=U;
          dep[v[i]]=dep[U]+1;
          if(siz[top[U]]<sz)
            {
              ++siz[top[U]];
              top[v[i]]=top[U];
            }
          dfs(v[i]);
        }
}
void df2(int U)
{
    num[U]=blo;
    for(int i=first[U];i;i=next[i])
      if(v[i]!=fa[U]&&top[v[i]]==top[U])
        df2(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;
}
struct UPT{int x,y,z;}CH[N];
struct ASK{int l,r,p,t;}Q[N];
bool operator < (const ASK &a,const ASK &b)
{
    if(num[a.l]==num[b.l])
      {
        if(num[a.r]==num[b.r])
          return a.t<b.t;
        return num[a.r]<num[b.r];
      }
    return num[a.l]<num[b.l];
}
int nu2[N],r[230],l[230],sumv[230],bl2=1,T[N];
void makeblock()
{
    int sz=sqrt(n); if(!sz) sz=0;
    r[0]=-1;
    for(;bl2*sz<n;++bl2)
      {
        l[bl2]=r[bl2-1]+1;
        r[bl2]=bl2*sz;
        for(int i=l[bl2];i<=r[bl2];++i)
          nu2[i]=bl2;
      }
    l[bl2]=r[bl2-1]+1;
    r[bl2]=n;
    for(int i=l[bl2];i<=r[bl2];++i)
      nu2[i]=bl2;
}
void Update(int x,int op)
{
    if(x>=n)
      return;
    T[x]+=op;
    if(!T[x])
      --sumv[nu2[x]];
    else if(op==1&&T[x]==1)
      ++sumv[nu2[x]];
}
bool vis[N];
void Work(int U,int V,int LCA)
{
    while(U!=LCA)
      {
        vis[U]^=1;
        Update(a[U],vis[U]?1:-1);
        U=fa[U];
      }
    while(V!=LCA)
      {
        vis[V]^=1;
        Update(a[V],vis[V]?1:-1);
        V=fa[V];
      }
}
int Query()
{
    for(int i=1;i<=bl2;++i)
      if(sumv[i]<r[i]-l[i]+1)
        for(int j=l[i];j<=r[i];++j)
          if(!T[j])
            return j;
}
bool op[N];
int anss[N];
int main()
{
    int x,y;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
      scanf("%d",&a[i]);
    for(int i=1;i<n;++i)
      {
         scanf("%d%d",&x,&y);
         AddEdge(x,y);
         AddEdge(y,x);
      }
    for(int i=1;i<=n;++i)
      {
        top[i]=i;
        siz[i]=1;
      }
    sz=(int)pow((double)n,2.0/3.0);
    dfs(1);
    for(int i=1;i<=n;++i)
      if(top[i]==i)
        {
          ++blo;
          df2(i);
        }
    memcpy(b,a,sizeof(int)*(n+1));
    for(int i=1;i<=m;++i)
      {
        scanf("%d",&op[i]);
        if(!op[i])
          {
            ++ec;
            scanf("%d%d",&CH[ec].x,&CH[ec].y);
            CH[ec].z=b[CH[ec].x];
            b[CH[ec].x]=CH[ec].y;

          }
        else
          {
            ++eq;
            scanf("%d%d",&Q[eq].l,&Q[eq].r);
            Q[eq].t=ec; Q[eq].p=eq;
          }
      }
    makeblock();
    sort(Q+1,Q+eq+1);
    for(int i=1;i<=Q[1].t;++i)
      a[CH[i].x]=CH[i].y;
    int LCA=lca(Q[1].l,Q[1].r);
    Work(Q[1].l,Q[1].r,LCA);
    Update(a[LCA],1);
    anss[Q[1].p]=Query();
    Update(a[LCA],-1);
    for(int i=2;i<=eq;++i)
      {
        if(Q[i-1].t<Q[i].t) for(int j=Q[i-1].t+1;j<=Q[i].t;++j)
          {
            if(vis[CH[j].x])
              {
                Update(CH[j].y,1);
                Update(a[CH[j].x],-1);
              }
            a[CH[j].x]=CH[j].y;
          }
        else for(int j=Q[i-1].t;j>Q[i].t;--j)
          {
            if(vis[CH[j].x])
              {
                Update(CH[j].z,1);
                Update(a[CH[j].x],-1);
              }
            a[CH[j].x]=CH[j].z;
          }
        Work(Q[i-1].l,Q[i].l,lca(Q[i-1].l,Q[i].l));
        Work(Q[i-1].r,Q[i].r,lca(Q[i-1].r,Q[i].r));
        LCA=lca(Q[i].l,Q[i].r);
        Update(a[LCA],1);
        anss[Q[i].p]=Query();
        Update(a[LCA],-1);
      }
    for(int i=1;i<=eq;++i)
      printf("%d\n",anss[i]);
    return 0;
}
时间: 2024-10-25 21:55:12

【树上莫队】【带修莫队】【权值分块】bzoj4129 Haruna’s Breakfast的相关文章

莫队 + 带修莫队

莫队其实就是一个带优化的暴力,通过将区间询问按一定规则进行排序,从而优化过程,求出答案. 举一例子:(例子不具备权威性,只是让读者了解莫队是干啥的) /* 输入四个区间 1 4 初始条件,L= R = 0, 将R遍历到4 需要走4步 L走一步,共5次 4 8 继承上一次 L 和 R 的值,L从1到4 需要3次,R从4到8,需4次, 总计8次 2 9 同理继承, L回退2次, R前进一次 总计3次 1 2 同理,L回退1次,R回退7次 总计8次 如果直接暴力,计算机将要计算 5+8+3+8=24次

【BZOJ-3052】糖果公园 树上带修莫队算法

3052: [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MBSubmit: 883  Solved: 419[Submit][Status][Discuss] Description Input Output Sample Input Sample Output 84 131 27 84 HINT Source Solution 树上带修莫队 本质还是树上莫队,详情可以转 BZOJ-3757苹果树 但是这里需要修改,就需要一些特殊的地方

【带修莫队】【权值分块】bzoj3196 Tyvj 1730 二逼平衡树

这题用了三种算法写: 分块+二分:O(n*sqrt(n*log(n)) 函数式权值分块:O(n*sqrt(n)) 带修莫队+权值分块:O(n5/3) 结果……复杂度越高的实际上跑得越快……最后这个竟然进第一页了…… #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int f,C; inline void R(int &

bzoj4129 Haruna’s Breakfast 树上带修莫队+分块

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4129 题解 考虑没有修改的序列上的版本应该怎么做: 弱化的题目应该是这样的: 给定一个序列,每次询问区间 \([l, r]\) 中元素的最小没有出现的自然数. 这个弱化的版本可以用离线+线段树二分水掉.但是这个做法显然不太好搬到树上做. 上面的弱化版还有一个莫队做法:可以用莫队维护出来每一个区间的每一个数的出现为次数.把出现过的数通过分块表示出来,于是查询的时候枚举每一个块,寻找第一个不满的

2120: 数颜色(带修莫队)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2120 2120: 数颜色 Time Limit: 6 Sec  Memory Limit: 259 MBSubmit: 10514  Solved: 4398[Submit][Status][Discuss] Description 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R

CF940F Machine Learning(带修莫队)

首先显然应该把数组离散化,然后发现是个带修莫队裸题,但是求mex比较讨厌,怎么办?其实可以这样求:记录每个数出现的次数,以及出现次数的出现次数.至于求mex,直接暴力扫最小的出现次数的出现次数为0的正整数,就一句话,这样看似会超时,实际上是O(√n)的复杂度.为什么?假设存在出现1,2,...,x的出现次数,则Σi(1<=i<=x)<=n,即x*(x+1)<=2*n,所以x至多是√n级别.很多人再把出现次数分块,根本没必要.然后考虑把数组分块的块大小,每次移动左指针,为O(n*块大

数颜色(带修莫队模板)

数颜色(luogu) Description 题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2. R P Col 把第P支画笔替换为颜色Col. 为了满足墨墨的要求,你知道你需要干什么了吗? 输入格式 第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数. 第2行N个整数,分别代表初始画笔排中第i支画笔的颜色. 第3行到第2+M行

【莫队算法】【权值分块】bzoj2223 [Coci 2009]PATULJCI

不带修改主席树裸题<=>莫队+权值分块裸题. 复杂度O(m*sqrt(n)). P.S.题目描述坑爹,第二个数是权值的范围. #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define N 300001 #define M 10001 int f,c; inline void R(int &x){ c=0;f=1; for(;c<'0'||c

【莫队算法】【权值分块】bzoj3920 Yuuna的礼物

[算法一] 暴力. 可以通过第0.1号测试点. 预计得分:20分. [算法二] 经典问题:区间众数,数据范围也不是很大,因此我们可以: ①分块,离散化,预处理出: <1>前i块中x出现的次数(差分): <2>第i块到第j块中的众数是谁,出现了多少次. 询问的时候,对于整块的部分直接获得答案:对于零散的部分,暴力统计每个数出现 的次数,加上差分的结果,尝试更新ans. 时间复杂度O(m*sqrt(n)), 空间复杂度O(n*sqrt(n)). ②考虑离线,莫队算法,转移的时候使用数据

【DFS序列】【莫队算法】【权值分块】bzoj2809 [Apio2012]dispatching

题意:在树中找到一个点i,并且找到这个点子树中的一些点组成一个集合,使得集合中的所有点的c之和不超过M,且Li*集合中元素个数和最大 首先,我们将树处理出dfs序,将子树询问转化成区间询问. 然后我们发现,对于单一节点来说,“使得集合中的所有点的c之和不超过M,且Li*集合中元素个数和最大”可以贪心地搞,即优先选择c较小的点.(<--这正是主席树/权值线段树/权值分块的工作) 但是我们需要枚举所有节点,从他们中选一个最大的. 既然有dfs序了,那么就是无修改的区间询问咯.(<--莫队的工作)