树上莫队小结

树上莫队首先是考虑将树上的问题转移成序列上的问题
考虑括号序,一个点在dfs进入它和退出去时分别记一次。这样,考虑原树中的一条路径(u,v)可以被在序列中表示成什么样子:设u的括号序更小,那么如果u是LCA(u,v),那么路径可以被表示成(st[u],st[v]),否则路径可以被表示成(ed[u],st[v]),无论怎样表示,我们都只统计出现在序列中出现奇数次的元素的信息。注意这样的话第2种情况我们并没有统计LCA的信息,所以需要单独判断。然后因为括号序的长度是原树大小的2倍,所以数组记得开大。

例题:
bzoj3757

比较裸的一道题目(笔者一开始妄图树上差分,但是没法去重)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m,a[maxn];
struct edge{
	int v,p;
}e[maxn<<1];
int h[maxn],cnt;
inline void add(int a,int b){
	e[++cnt].p=h[a];
	e[cnt].v=b;
	h[a]=cnt;
	e[++cnt].p=h[b];
	e[cnt].v=a;
	h[b]=cnt;
}
int st[maxn],ed[maxn],pos[maxn],tot,dep[maxn];
int f[maxn][20];
void dfs1(int u,int fa){
	tot++;
	st[u]=tot;pos[tot]=u;dep[u]=dep[fa]+1;
	f[u][0]=fa;
	for(int i=1;f[u][i-1];i++)f[u][i]=f[f[u][i-1]][i-1];
	for(int i=h[u];i;i=e[i].p){
		int v=e[i].v;
		if(v==fa)continue;
		dfs1(v,u);
	}
	ed[u]=++tot;pos[tot]=u;
}
struct node{
	int l,r,pos,a,b,lca,id;
}q[maxn];
int lim;
int out[maxn];
bool cmp(node a,node b){
	if(a.pos==b.pos){
		if(a.pos&1)return a.r<b.r;
		return a.r>b.r;
	}
	return a.pos<b.pos;
}
int col[maxn],vis[maxn],ans;
void add(int x){
	vis[pos[x]]++;
	if(vis[pos[x]]&1){
		col[a[pos[x]]]++;
		if(col[a[pos[x]]]==1&&pos[x]){
			//printf("%d %d !\n",x,pos[x]);
			ans++;
		}
	}
	else{
		col[a[pos[x]]]--;
		if(col[a[pos[x]]]==0&&pos[x]){
			//printf("%d %d ?\n",x,pos[x]);
			ans--;
		}
	}
}
void remove(int x){
	vis[pos[x]]--;
	if(vis[pos[x]]&1){
		col[a[pos[x]]]++;
		if(col[a[pos[x]]]==1&&pos[x]){
			//printf("%d %d !\n",x,pos[x]);
			ans++;
		}
	}
	else{
		col[a[pos[x]]]--;
		if(col[a[pos[x]]]==0&&pos[x]){
			//printf("%d %d ?\n",x,pos[x]);
			ans--;
		}
	}
}
inline int lca(int a,int b){
	if(dep[a]>dep[b])swap(a,b);
	for(int i=19;i>=0;i--){
		if(dep[a]<=dep[f[b][i]])b=f[b][i];
	}
	if(a==b)return a;
	for(int i=19;i>=0;i--){
		if(f[a][i]!=f[b][i]){a=f[a][i];b=f[b][i];}
	}
	return f[a][0];
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++){
		int a=read(),b=read();
		add(a,b);
	}
	dfs1(0,0);
	lim=sqrt(2*m);
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		if(st[x]>st[y])swap(x,y);
		q[i].lca=lca(x,y);
		//printf("%d %d %d %d %d\n",x,q[i].lca,y,st[x],st[y]);
		if(q[i].lca==x)
		q[i].l=st[x];
		else
		q[i].l=ed[x];
		q[i].r=st[y],q[i].a=read(),q[i].b=read();
		q[i].pos=q[i].l/lim+1;q[i].id=i;
	}
	sort(q+1,q+1+m,cmp);
	int l=1,r=1;
	/*for(int i=1;i<=tot;i++)
	printf("%d ",pos[i]);
	puts("");*/
	for(int i=1;i<=m;i++){
		while(r<q[i].r)add(++r);
		while(r>q[i].r)remove(r--);
		while(l<q[i].l)remove(l++);
		while(l>q[i].l)add(--l);
		out[q[i].id]=ans;
		if(!col[a[q[i].lca]]){out[q[i].id]++;}
		if(q[i].a!=q[i].b){
			col[a[q[i].lca]]++;
			if(col[q[i].a]&&col[q[i].b])out[q[i].id]--;
			col[a[q[i].lca]]--;
		}
		/*for(int i=1;i<=n;i++)
		printf("%d ",vis[i]);
		puts("");
		printf("%d %d %d %d %d\n",ans,q[i].a,q[i].b,q[i].l,q[i].r);*/
	}
	for(int i=1;i<=m;i++)printf("%d\n",out[i]);
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牙科就诊管理系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了用户在线查看数据。管理员管理病例管理、字典管理、公告管理、药单管理、药品管理、药品收藏管理、药品评价管理、药品订单管理、牙医管理、牙医收藏管理、牙医评价管理、牙医挂号管理、用户管理、管理员管理等功能。牙科就诊管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 管理员在后台主要管理病例管理、字典管理、公告管理、药单管理、药品管理、药品收藏管理、药品评价管理、药品订单管理、牙医管理、牙医收藏管理、牙医评价管理、牙医挂号管理、用户管理、管理员管理等。 牙医列表页面,此页面提供给管理员的功能有:查看牙医、新增牙医、修改牙医、删除牙医等。公告信息管理页面提供的功能操作有:新增公告,修改公告,删除公告操作。公告类型管理页面显示所有公告类型,在此页面既可以让管理员添加新的公告信息类型,也能对已有的公告类型信息执行编辑更新,失效的公告类型信息也能让管理员快速删除。药品管理页面,此页面提供给管理员的功能有:新增药品,修改药品,删除药品。药品类型管理页面,此页面提供给管理员的功能有:新增药品类型,修改药品类型,删除药品类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值