其他分享
首页 > 其他分享> > 题解-AHOI2022 钥匙

题解-AHOI2022 钥匙

作者:互联网

通过这道题发现我是春春的滴嫩儿。

前两题一共花了我4.5h,也就是说我要去AH的话切完前两题比赛就结束了后面的题看都没得看。

还是要加强码力和熟练度啊。


首先考虑对每种颜色分别求解,那么我们可以把钥匙和宝箱看成一个括号匹配(怎么又是它),考虑在匹配的位置记入答案。那么可以枚举一对 \((1,2)\),看是否是一组匹配。然后计入答案就比较简单了,就是一个矩阵加,离线扫描线+树状数组即可。考虑如何判断?我们先把同一个颜色的数拿出来建虚树,然后考虑在虚树上往上跳,如果跳到某个位置不能匹配了就停止。容易发现跳的次数是常数(或者说是钥匙和宝箱个数的较小值)。于是这道题就做完了。

写代码的时候注意卡常。

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int maxn=5e5+5;
template<typename T>
void read(T &x){
	T sgn=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')sgn=-1;
	for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
	x*=sgn;
}
int n,m,op[maxn],col[maxn],dfn[maxn],pos[maxn],num,dep[maxn],f[maxn][21],sz[maxn];
int node[maxn],cnt,stk[maxn],top,bit[maxn];
vector<int>vec[maxn],zz[2][maxn],G[maxn];
vector<pair<int,int>>qq[maxn];
int ans[maxn<<1],pre[maxn];
struct Rec{
	int x,l,r,w;
	friend bool operator<(Rec a, Rec b){
		return a.x<b.x;
	}
}q[maxn*2*5];
int oo;
void ins(int x,int w){
	while(x<=n){
		bit[x]+=w;
		x+=x&-x;
	}
}
int qry(int x){
	int ret=0;
	while(x){
		ret+=bit[x];
		x-=x&-x;
	}
	return ret;
}
int eul[maxn<<1][21],jj,lg[maxn<<1],fir[maxn];
void dfs1(int u){
	dfn[pos[u]=++num]=u;
	sz[u]=1;
	for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1];
	eul[fir[u]=++jj][0]=u;
	for(int v:vec[u])if(v!=f[u][0]){
		dep[v]=dep[u]+1;
		f[v][0]=u;
		dfs1(v);
		eul[++jj][0]=u;
		sz[u]+=sz[v];
	}
}
int Min(int a,int b){
	if(dep[a]<dep[b])return a;
	return b;
}
void init(){
	for(int j=1;j<=20;j++){
		for(int i=1;i+(1<<j)<=jj;i++){
			eul[i][j]=Min(eul[i][j-1],eul[i+(1<<(j-1))][j-1]);
		}
	}
	lg[0]=-1;
	for(int i=1;i<=jj;i++)lg[i]=lg[i>>1]+1;
}
int jump(int u,int d){
	for(int i=0;i<=20&&d;i++)if(d&(1<<i))u=f[u][i],d-=1<<i;
	return u;
}
int LCA(int u,int v){
	u=fir[u];v=fir[v];
	if(u>v)swap(u,v);
	int k=lg[v-u+1];
	return Min(eul[u][k],eul[v-(1<<k)+1][k]);
}
bool cmp(int a,int b){
	return pos[a]<pos[b];
}
void add_edge(int u,int v){
	G[u].pb(v);
}
void ins(int u){
	if(top==0)return stk[top=1]=u,void();
	int lca=LCA(u,stk[top]);
	while(top>1&&dep[stk[top-1]]>dep[lca])add_edge(stk[top-1],stk[top]),top--;
	if(dep[lca]<dep[stk[top]])add_edge(lca,stk[top]),top--;
	if(!top||stk[top]!=lca)stk[++top]=lca;
	stk[++top]=u;
}
void build_virtual_tree(){
	sort(node+1,node+1+cnt,cmp);
	if(node[1]!=1)stk[top=1]=1;
	else top=0;
	for(int i=1;i<=cnt;i++)ins(node[i]);
	while(top>1)add_edge(stk[top-1],stk[top]),top--;
}
void dfs3(int u,int Fa,int cc){
	for(int v:G[u])if(v!=Fa){
		pre[v]=pre[u];
		if(col[u]==cc)pre[v]=u;
		dfs3(v,u,cc);
	}G[u].clear();
}
bool check(int u,int v,int cc){
	int lca=LCA(u,v);
	int sum=1;
	assert(col[u]==cc&&col[v]==cc);
	do{
		u=pre[u];
		if(dep[u]<dep[lca])break;
		if(op[u]==1)sum++;
		else sum--;
		if(u==lca){
			break;
		}
		if(sum<=0)return false;
	}while(dep[u]>dep[lca]);
	if(lca==v){
		return sum==0;
	}
	int ssum=-1;
	while(dep[v]>dep[lca]){
		v=pre[v];
		if(dep[v]<=dep[lca])break;
		if(op[v]==1)ssum++;
		else ssum--;
		if(ssum>=0)return false;
		if(ssum<-5)return false;
	}
	return sum+ssum==0;
}
int main(){
	read(n);read(m);
	for(int i=1;i<=n;i++){
		read(op[i]);read(col[i]);
		zz[op[i]-1][col[i]].pb(i);
	}
	for(int i=1,u,v;i<n;i++){
		read(u);read(v);
		vec[u].pb(v);
		vec[v].pb(u);
	}
	dep[1]=1;dfs1(1);init();
	for(int i=1;i<=n;i++)if(zz[0][i].size()&&zz[1][i].size()){
		cnt=0;
		for(int j:zz[0][i])node[++cnt]=j;
		for(int j:zz[1][i])node[++cnt]=j;
		build_virtual_tree();
		dfs3(1,0,i);
		for(int k:zz[1][i])for(int j:zz[0][i]){
			if(!check(j,k,i))continue;
			int lca=LCA(j,k);
			if(lca==j){
				int l=jump(k,dep[k]-dep[j]-1);
				q[++oo]=Rec{1,pos[k],pos[k]+sz[k]-1,1};
				q[++oo]=Rec{pos[l],pos[k],pos[k]+sz[k]-1,-1};
				q[++oo]=Rec{pos[l]+sz[l],pos[k],pos[k]+sz[k]-1,1};
			}else if(lca==k){
				int l=jump(j,dep[j]-dep[k]-1);
				q[++oo]=Rec{pos[j],1,pos[l]-1,1};
				q[++oo]=Rec{pos[j],pos[l]+sz[l],n,1};
				q[++oo]=Rec{pos[j]+sz[j],1,pos[l]-1,-1};
				q[++oo]=Rec{pos[j]+sz[j],pos[l]+sz[l],n,-1};	
			}else{
				q[++oo]=Rec{pos[j],pos[k],pos[k]+sz[k]-1,1};
				q[++oo]=Rec{pos[j]+sz[j],pos[k],pos[k]+sz[k]-1,-1};	
			}
		}
	}
	for(int i=1;i<=m;i++){
		int s,t;
		read(s);read(t);
		qq[pos[s]].pb(make_pair(pos[t],i));
	}
	sort(q+1,q+1+oo);
	for(int i=1,j=1;i<=n;i++){
		while(j<=oo&&q[j].x<=i){
			if(q[j].l>q[j].r){
				j++;
				continue;
			}
			ins(q[j].l,q[j].w);
			ins(q[j].r+1,-q[j].w);
			j++;
		}
		for(int k=0;k<(int)qq[i].size();k++){
			ans[qq[i][k].second]=qry(qq[i][k].first);
		}
	}
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}

标签:ch,int,题解,top,lca,dep,maxn,AHOI2022,钥匙
来源: https://www.cnblogs.com/zcr-blog/p/16293444.html