从莫队到树上莫队

从莫队到树上莫队

  1. 莫队

    1. 离线处理查询问题的解决方法。

    2. 询问离线,第一关键字 l l l所在块,第二关键字 r r r的绝对位置。

    3. 设 块 大 小 为 n x , 左 端 点 移 动 n 1 + x 次 , 右 端 点 移 动 n 1 − x + 1 次 , x = 0.5 设块大小为n^x,左端点移动n^{1+x}次,右端点移动n^{1-x+1}次,x=0.5 nx,n1+xn1x+1x=0.5

    4. 核心代码

      //创建块
      m=sqrt(n);bel[0]=1;
      for(int i=1;i<=n;i++)
      {
          scanf("%d",&a[i]);
          bel[i]=i/m+1;
      }
      //排序
      bool operator<(nod a,nod b)
      {
      	return bel[a.l]!=bel[b.l] ? bel[a.l]<bel[b.l] : a.r<b.r;
      }
      //莫队
      int nl=0,nr=0;ll res=0;
      for(int i=1;i<=q;i++)
      {
      	int l=query[i].l,r=query[i].r;
      	while(nr<r) nr++,cntr[a[nr]]++,res+=cntl[a[nr]];
      	while(nl<l) nl++,cntl[a[nl]]++,res+=cntr[a[nl]];
      	while(nr>r) res-=cntl[a[nr]],cntr[a[nr]]--,nr--;
      	while(nl>l) res-=cntr[a[nl]],cntl[a[nl]]--,nl--;
      	ans[query[i].id]+=(((ll)query[i].flag)*res);
      }
      
  2. 带修莫队

    1. 带修莫队显然就是增加修改操作的莫队

    2. 询问离线,第一关键字l所在块,第二关键字r所在块,第三关键字时间。

    3. 设 块 大 小 为 n x , 左 端 点 移 动 n 1 + x , 右 端 点 移 动 n 2 − x + n 1 + x , 时 间 端 点 移 动 n 1 − x + 1 − x + 1 , x = 2 3 设块大小为n^x,左端点移动n^{1+x},右端点移动n^{2-x}+n^{1+x},时间端点移动n^{1-x+1-x+1},x=\frac 2 3 nx,n1+x,n2x+n1+x,n1x+1x+1,x=32

    4. //创建块
      m=pow(n,2.0/3.0);
      for(int i=1;i<=n;i++)
      {
      	scanf("%d",&col[i]);temp[i]=col[i];
      	bel[i]=((i-1)/m)+1;
      }
      //排序
      bool operator<(nod1 a,nod1 b)
      {
          if(bel[a.l]!=bel[b.l]) return bel[a.l]<bel[b.l];
          if(bel[a.r]!=bel[b.r]) return bel[a.r]<bel[b.r];
          return a.pos<b.pos;
      }
      //更新状态
      //注意,更改时间操作的时候需要维护前一个时间是谁,方便回退状态
      void update(int now)
      {
          if(cnt[col[now]]==0) res++;
          cnt[col[now]]++;
      }
      void erase(int now)
      {
          cnt[col[now]]--;
          if(cnt[col[now]]==0) res--;
      }
      void update(int now,int nl,int nr)
      {
          if(nl<=modify[now].u && modify[now].u<=nr) erase(modify[now].u);
          col[modify[now].u]=modify[now].now;
          if(nl<=modify[now].u && modify[now].u<=nr) update(modify[now].u);
      }
      void erase(int now,int nl,int nr)
      {
          if(nl<=modify[now].u && modify[now].u<=nr) erase(modify[now].u);
          col[modify[now].u]=modify[now].pre;
          if(nl<=modify[now].u && modify[now].u<=nr) update(modify[now].u);
      }
      //第一步可以强行更新,防止边界出错
      int nl=query[1].l,nr=query[1].r,nt=query[1].pos;
      for(int i=nl;i<=nr;i++) update(i);
      for(int i=1;i<=nt;i++) update(i,nl,nr);
      ans[query[1].id]=res;
      for(int i=2;i<=tot1;i++)
      {
          int l=query[i].l,r=query[i].r,t=query[i].pos;
          while(nr<r) nr++,update(nr);
          while(l<nl) nl--,update(nl);
          while(r<nr) erase(nr),nr--;
          while(nl<l) erase(nl),nl++;
          while(nt<t) nt++,update(nt,l,r);
          while(t<nt) erase(nt,l,r),nt--;
          ans[query[i].id]=res;
      }
      
  3. 树上莫队

    1. 树上莫队NB!

    2. 考虑全 d f s dfs dfs序,点 p p p到点 q q q 之间只出现过一次的点,再加上点 p p p与点 q q q l c a lca lca,就是p到q路径上的点。

    3. 然后莫队就行了

    4. 这里 比较需要注意的是query的时候l和r到底选哪个,分情况讨论。

    5. //CF GYM 100962F
      #include<iostream>
      #include<stdio.h>
      #include<string.h>
      #include<algorithm>
      #include<math.h>
      #define maxn 200005
      using namespace std;
      typedef long long ll;
      struct nod
      {
      	int l,r,id;
      	nod(int a,int b,int c)
      	{
      		l=a;
      		r=b;
      		id=c;
      	}
      	nod(){}
      	
      }query[maxn];
      int n,q,tot,bel[maxn],m;
      int head[maxn],nex[maxn],to[maxn],w[maxn];
      void add(int x,int y,int z)
      {
      	to[++tot]=y; nex[tot]=head[x]; w[tot]=z; head[x]=tot;
      }
      bool operator<(nod a,nod b)
      {
      	return bel[a.l]!=bel[b.l] ? bel[a.l]<bel[b.l] : a.r<b.r;
      }
      
      int dfn[maxn],val[maxn],in[maxn],out[maxn];
      void dfs(int now,int fa)
      {
      	in[now]=++tot;
      	dfn[tot]=now;
      	for(int i=head[now];i;i=nex[i])
      	{
      		if(to[i]!=fa)
      		{
      			val[to[i]]=min(n,w[i]);
      			dfs(to[i],now);
      		}
      	}
      	out[now]=++tot;
      	dfn[tot]=now;
      }
      int l[maxn],r[maxn];
      int cnt[maxn],sum[maxn],vis[maxn];
      void update(int now)
      {
      	if(vis[now]==0)
      	{
      		vis[now]=1;
      		
      		cnt[val[now]]++;
      		if(cnt[val[now]]==1)
      			sum[bel[val[now]]]++;
      	}
      	else
      	{
      		vis[now]=0;
      		
      		cnt[val[now]]--;
      		if(cnt[val[now]]==0)
      			sum[bel[val[now]]]--;
      	}
      }
      int ans[maxn];
      int getans()
      {
      	for(int i=1;i<=(n-1)/m+1;i++)
      	{
      		if(sum[i]!=(r[i]-l[i]+1))
      		{
      			for(int j=l[i];j<=r[i];j++)
      			{
      				if(!cnt[j])
      				{
      					return j-1;
      				}
      			}
      		}
      	}
      	return n;
      }
      int main()
      {
      	memset(l,0x3f,sizeof(l));
      	scanf("%d%d",&n,&q); 
      	
      	m=320;
      	for(int i=1;i<=n;i++)
      	{
      		bel[i]=(i-1)/m+1;
      		l[bel[i]]=min(l[bel[i]],i);
      		r[bel[i]]=max(r[bel[i]],i);
      	}
      		
      	for(int i=1;i<n;i++)
      	{
      		int x,y,z;
      		scanf("%d%d%d",&x,&y,&z); 
      		z++;
      		add(x,y,z);
      		add(y,x,z);
      	}
      	
      	tot=0;
      	dfs(1,-1);
      		
      	for(int i=1;i<=q;i++)
      	{
      		int u,v;
      		scanf("%d%d",&u,&v);
      		query[i].id=i;
      		if(in[u]>in[v]) swap(u,v);
      		
      		//这里是边权 
      		query[i].l=in[u]+1;
      		query[i].r=in[v];
      		
      		//如果是点权 
      		//if(lca==u){query[i].l = in[u];query[i].r = in[v];}
              //else {query[i].l = out[u];query[i].r = in[v];}
      	}
      	
      	sort(query+1,query+1+q);
      	
      	int nl=1,nr=0;
      	for(int i=1;i<=q;i++)
      	{
      		int l=query[i].l,r=query[i].r;
      		while(nr<r) nr++,update(dfn[nr]);
      	    while(l<nl) nl--,update(dfn[nl]);
      	    while(r<nr) update(dfn[nr]),nr--;
      	    while(nl<l) update(dfn[nl]),nl++;
      	    //本题是边权不含lca 
      		//如果是点权要加一句 if(!cnt[val[query[i].lca]]) update()
      		ans[query[i].id]=getans();
      	}
      	
      	for(int i=1;i<=q;i++)
      	{
      		printf("%d\n",ans[i]);
      	}
      }
      
  4. 其他

    1. o ( 1 ) o(1) o(1)操作, o ( n ) o(\sqrt n) o(n )的分块和莫队更配哦。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值