SP10707 COT2 - Count on a tree II(欧拉序 树上莫队)

点击跳转

题意:

在这里插入图片描述

思路:

update!!!我感觉这可能是括号序,大概不同的叫法吧,迷了

  • 简化版本是询问区间 [ l , r ] [l,r] [l,r]里不同数的个数,可以用莫队,树状数组,主席树多种方法做
  • 将该问题搬到树上后,依旧可以用莫队求解
  • 现在考虑怎么样将树上问题转化为序列问题, d f s dfs dfs序列,树链剖分等等
  • d f s dfs dfs序是行不通的,因为无法处理 l c a lca lca的贡献
  • 考虑欧拉序,即当访问到 u u u时,将 u u u加入序列;访问 u u u的子树;回溯到 u u u时,再将 u u u加入序列;
  • f i r s t [ x ] first[x] first[x]表示 x x x在欧拉序第一次出现的下标, l a s [ x ] las[x] las[x]表示最后一次出现的下标
  • 不妨设 f i r s t [ x ] < f i r s t [ y ] first[x]<first[y] first[x]<first[y]
  • 如果 l c a ( x , y ) = = x lca(x,y)==x lca(x,y)==x的话, x − > y x->y x>y路径上的点就是区间 [ f i r s t [ x ] , f i r s t [ y ] ] [first[x],first[y]] [first[x],first[y]]内的点
  • 否则,就是 [ l a s [ x ] , f i r s t [ y ] ] [las[x],first[y]] [las[x],first[y]]加上 l c a ( x , y ) lca(x,y) lca(x,y)
  • 画图就可以验证,然后用莫队就可以啦。

代码

const int maxn=100010,mod=1e9+7,inf=0x3f3f3f3f;

int fa[maxn][25], dep[maxn],w[maxn];
int e[maxn],ne[maxn],m;
int h[maxn], idx = 0,n,root;
vector<int>num;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void init()
{
    memset(h, -1, sizeof h);
    idx = 0;
}
void bfs(int root)
{
    queue<int>q;
    memset(dep, 0x3f, sizeof dep);
    dep[0] = 0; 
    dep[root] = 1;
    q.push(root);
    while(!q.empty())
    {
        int t = q.front();
        q.pop();
        for(int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if(dep[j] > dep[t] + 1)
            {
                dep[j] = dep[t] + 1;
                q.push(j);
                fa[j][0] = t; 
                for(int k = 1; k <= 15; k++)
                    fa[j][k] = fa[fa[j][k - 1]][k - 1];
                }
        }
    }
}

int lca(int a, int b)
{
    if(dep[a] < dep[b]) swap(a, b);
    for(int k = 15; k >= 0; k--)
         if(dep[fa[a][k]] >= dep[b]) a = fa[a][k]; 
    if(a == b) return a;
    for(int k = 15; k >= 0; k--)
        if(fa[a][k] != fa[b][k]) a = fa[a][k], b = fa[b][k];
    return fa[a][0];
}
int seq[maxn],top,first[maxn],las[maxn];

void dfs(int u,int fa){
	seq[++top]=u;
	first[u]=top;
	for(int i=h[u];~i;i=ne[i]){
		int j=e[i];
		if(j!=fa) dfs(j,u);
	}
	seq[++top]=u;
	las[u]=top;
}

struct node{
	int id,l,r,p;
}q[maxn];
int len,ans[maxn],res=0,st[maxn],cnt[maxn];

bool cmp(node a,node b){
	if(a.l/len==b.l/len) return a.r<b.r;
	return a.l/len<b.l/len;
}
void add(int x){
	//x=seq[x];
	st[x]^=1;
	if(st[x]==0){
		cnt[w[x]]--;
		if(!cnt[w[x]]) res--;
	}
	else{
		if(!cnt[w[x]]) res++;
		cnt[w[x]]++;
	}
}

void del(int x){
	x=seq[x];
	st[x]^=1;
	if(st[x]==0){
		cnt[w[x]]--;
		if(!cnt[w[x]]) res--;
	}
	else{
		if(!cnt[w[x]]) res++;
		cnt[w[x]]++;
	}
}
int main(){
	init();
	n=read,m=read;
	rep(i,1,n) w[i]=read,num.push_back(w[i]);
	sort(num.begin(),num.end());
	num.erase(unique(num.begin(),num.end()),num.end());
	rep(i,1,n)
		w[i]=lower_bound(num.begin(),num.end(),w[i])-num.begin();
	rep(i,1,n-1){
		int u=read,v=read;
		add(u,v);add(v,u);
	}
	dfs(1,-1);
	bfs(1);
	rep(i,1,m){
		int a=read,b=read;
		if(first[a]>first[b]) swap(a,b);
		int p=lca(a,b);
		if(a==p) q[i]={i,first[a],first[b]};
		else q[i]={i,las[a],first[b],p};
	}
	len=sqrt(top);
	sort(q+1,q+1+m,cmp);
	int nowl=1,nowr=0;
	rep(i,1,m){
		int id=q[i].id,ql=q[i].l,qr=q[i].r,p=q[i].p;
	//	cout<<id<<" "<<ql<<" "<<qr<<" "<<p<<endl;
		while(nowr<qr) add(seq[++nowr]);
		while(nowr>qr) add(seq[nowr--]);
		while(nowl<ql) add(seq[nowl++]);
		while(nowl>ql) add(seq[--nowl]);	
		if(p) add(p);
		ans[id]=res;
		if(p) add(p);
		//rep(i,1,n) cout<<cnt[i]<<" ";
		//puts("");
	}
	rep(i,1,m) cout<<ans[i]<<"\n";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值