题解-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