题解 [AHOI2022] 钥匙

传送门

啥也不会实锤了
先考虑一个类似括号匹配的配对策略
怎么去找这些匹配呢?
发现同色钥匙非常少
那么把每种颜色的钥匙和宝箱拉出来建虚树

  • 关于建虚树:一个比较省事的做法是将点集中的点拉出来按 dfs 序排序,对相邻两个求 lca
    这样最终在虚树上的点都被找出来了,那么按照 dfs 序的包含关系连边就可以了

然后分类讨论钥匙和宝箱的父子关系
对 s 做扫描线,维护所有 v 的答案
那么一对钥匙,宝箱的贡献就是形如进入 s 子树时给一段 dfs 序做加加减减
复杂度 \(O(n\log n)\),貌似带 10 的常数

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
int t[N], c[N];
int head[N], ecnt;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}

namespace force{
	map<int, int> mp;
	pair<int, int> sta[N];
	int lg[N], fa[21][N], dep[N], top;
	void dfs(int u, int pa) {
		// cout<<"dfs: "<<u<<' '<<pa<<endl;
		for (int i=1; dep[u]>=1<<i; ++i)
			fa[i][u]=fa[i-1][fa[i-1][u]];
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (v==pa) continue;
			fa[0][v]=u;
			dep[v]=dep[u]+1;
			dfs(v, u);
		}
	}
	int lca(int a, int b) {
		if (dep[a]<dep[b]) swap(a, b);
		while (dep[a]>dep[b]) a=fa[lg[dep[a]-dep[b]]-1][a];
		if (a==b) return a;
		for (int i=lg[dep[a]]-1; ~i; --i) 
			if (fa[i][a]!=fa[i][b])
				a=fa[i][a], b=fa[i][b];
		return fa[0][a];
	}
	void solve() {
		dep[1]=1; dfs(1, 0);
		for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
		for (int i=1,u,v,ans; i<=m; ++i) {
			u=read(); v=read();
			mp.clear(); top=ans=0;
			int t=lca(u, v);
			while (v!=t) sta[++top]={::t[v], c[v]}, v=fa[0][v];
			while (1) {
				if (::t[u]==1) ++mp[c[u]];
				else if (mp.find(c[u])!=mp.end() && mp[c[u]]) --mp[c[u]], ++ans;
				if (u==t) break;
				u=fa[0][u];
			}
			while (top) {
				if (sta[top].fir==1) ++mp[sta[top].sec];
				else if (mp.find(sta[top].sec)!=mp.end() && mp[sta[top].sec]) --mp[sta[top].sec], ++ans;
				--top;
			}
			printf("%d\n", ans);
		}
	}
}

namespace task{
	ll bit[N], ans[N*2], dlt;
	int lg[N], id[N], siz[N], fa[21][N], dep[N], sta[N], tem[N], top, tot;
	vector<pair<int, int>> buc[N], in_add[N], in_del[N], out_add[N], out_del[N], que[N];
	int anc(int x, int k);
	inline void add(int i, ll dat) {for (; i<=n; i+=i&-i) bit[i]+=dat;}
	inline void add(int l, int r, ll dat) {add(l, dat); add(r+1, -dat);}
	inline ll query(int i) {ll ans=0; for (; i; i-=i&-i) ans+=bit[i]; return ans;}
	struct tree{
		bool *vis;
		int tot, cnt;
		vector<int> *to;
		int *id, *dep, **fa;
		void link(int u, int v) {to[u].pb(v); to[v].pb(u);}
		void resize(int siz) {
			tot=siz;
			to=new vector<int>[siz+1];
			id=new int[siz+1]; dep=new int[siz+1];
			fa=new int*[lg[siz]]; vis=new bool[siz+1];
			for (int i=0; i<lg[siz]; ++i) {
				fa[i]=new int[siz+1];
				for (int j=0; j<=siz; ++j) fa[i][j]=0;
			}
		}
		void dfs1(int u, int pa) {
			// cout<<"dfs1: "<<u<<' '<<pa<<endl;
			for (int i=1; dep[u]>=1<<i; ++i)
				fa[i][u]=fa[i-1][fa[i-1][u]];
			for (auto& v:to[u]) if (v!=pa) {
				fa[0][v]=u;
				dep[v]=dep[u]+1;
				dfs1(v, u);
			}
		}
		int lca(int a, int b) {
			if (dep[a]<dep[b]) swap(a, b);
			while (dep[a]>dep[b]) a=fa[lg[dep[a]-dep[b]]-1][a];
			if (a==b) return a;
			for (int i=lg[dep[a]]-1; ~i; --i) 
				if (fa[i][a]!=fa[i][b])
					a=fa[i][a], b=fa[i][b];
			return fa[0][a];
		}
		void calc(int u, int v) {
			// cout<<"calc: "<<id[u]<<' '<<id[v]<<endl;
			int t=lca(u, v);
			// cout<<"lca: "<<t<<endl;
			if (t==u) {
				int g=anc(id[v], task::dep[id[v]]-task::dep[id[u]]-1);
				add(task::id[id[v]], task::id[id[v]]+task::siz[id[v]]-1, 1);
				in_del[g].pb({task::id[id[v]], task::id[id[v]]+task::siz[id[v]]-1});
				// out_add[g].pb({task::id[id[v]], task::id[id[v]]+task::siz[id[v]]-1});
			}
			else if (t==v) {
				int g=anc(id[u], task::dep[id[u]]-task::dep[id[v]]-1);
				in_add[id[u]].pb({1, n});
				in_del[id[u]].pb({task::id[g], task::id[g]+task::siz[g]-1});
				// out_del[id[u]].pb({1, n});
				// out_add[id[u]].pb({task::id[g], task::id[g]+task::siz[g]-1});
			}
			else {
				in_add[id[u]].pb({task::id[id[v]], task::id[id[v]]+task::siz[id[v]]-1});
				// out_del[id[u]].pb({task::id[id[v]], task::id[id[v]]+task::siz[id[v]]-1});
			}
		}
		void dfs2(int u, int fa, int rot) {
			// cout<<"dfs2: "<<u<<endl;
			bool op=0;
			if (vis[u]) {
				if (t[id[u]]==1) ++cnt;
				else if (cnt) {
					op=1;
					if (--cnt==0) {calc(rot, u); ++cnt; return ;}
				}
			}
			for (auto& v:to[u]) if (v!=fa) dfs2(v, u, rot);
			if (vis[u]) {
				if (t[id[u]]==1) --cnt;
				else if (op) ++cnt;
			}
		}
		void solve() {
			if (!tot) return ;
			// cout<<"tot: "<<tot<<endl;
			dep[1]=1; dfs1(1, 0);
			for (int i=1; i<=tot; ++i) if (vis[i]&&t[id[i]]==1) dfs2(i, 0, i);
		}
	}tr[N];
	void dfs1(int u, int pa) {
		id[u]=++tot; siz[u]=1;
		for (int i=1; dep[u]>=1<<i; ++i)
			fa[i][u]=fa[i-1][fa[i-1][u]];
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (v==pa) continue;
			fa[0][v]=u;
			dep[v]=dep[u]+1;
			dfs1(v, u);
			siz[u]+=siz[v];
		}
	}
	int lca(int a, int b) {
		if (dep[a]<dep[b]) swap(a, b);
		while (dep[a]>dep[b]) a=fa[lg[dep[a]-dep[b]]-1][a];
		if (a==b) return a;
		for (int i=lg[dep[a]]-1; ~i; --i) 
			if (fa[i][a]!=fa[i][b])
				a=fa[i][a], b=fa[i][b];
		return fa[0][a];
	}
	int anc(int x, int k) {
		for (int i=lg[k]-1; ~i; --i)
			if (k>=1<<i) x=fa[i][x], k-=1<<i;
		return x;
	}
	void dfs2(int u, int fa) {
		// cout<<"dfs2: "<<u<<endl;
		for (auto& it:in_add[u])
			if (it.fir==1&&it.sec==n) ++dlt;
			else add(it.fir, it.sec, 1);
		for (auto& it:in_del[u])
			if (it.fir==1&&it.sec==n) --dlt;
			else add(it.fir, it.sec, -1);
		for (auto& it:que[u]) ans[it.sec]=query(id[it.fir])+dlt;
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (v==fa) continue;
			dfs2(v, u);
		}
		for (auto& it:in_del[u])
			if (it.fir==1&&it.sec==n) ++dlt;
			else add(it.fir, it.sec, 1);
		for (auto& it:in_add[u])
			if (it.fir==1&&it.sec==n) --dlt;
			else add(it.fir, it.sec, -1);
	}
	void solve() {
		dep[1]=1; dfs1(1, 0);
		// cout<<"id: "; for (int i=1; i<=n; ++i) cout<<id[i]<<' '; cout<<endl;
		for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
		for (int i=1; i<=n; ++i) buc[c[i]].pb({t[i], i});
		for (int i=1; i<=n; ++i) {
			// cout<<"i: "<<i<<endl;
			tot=top=0;
			sort(buc[i].begin(), buc[i].end(), [](pair<int, int> a, pair<int, int> b){return id[a.sec]<id[b.sec];});
			for (auto& it:buc[i]) tem[++tot]=it.sec;
			for (int j=1; j<buc[i].size(); ++j) tem[++tot]=lca(buc[i][j-1].sec, buc[i][j].sec);
			sort(tem+1, tem+tot+1, [](int a, int b){return id[a]<id[b];});
			tot=unique(tem+1, tem+tot+1)-tem-1;
			tr[i].resize(tot);
			for (int j=1; j<=tot; ++j) tr[i].id[j]=tem[j], tr[i].vis[j]=(c[tem[j]]==i);
			sta[++top]=1;
			for (int j=2; j<=tot; ++j) {
				while (id[tem[j]]>=id[tem[sta[top]]]+siz[tem[sta[top]]]) --top;
				tr[i].link(sta[top], j); //, cout<<"link: "<<tem[sta[top]]<<' '<<tem[j]<<endl;
				sta[++top]=j;
			}
			tr[i].solve();
		}
		for (int i=1,u,v; i<=m; ++i) {
			u=read(); v=read();
			que[u].pb({v, i});
		}
		dfs2(1, 0);
		for (int i=1; i<=m; ++i) printf("%lld\n", ans[i]);
	}
}

signed main()
{
	freopen("keys.in", "r", stdin);
	freopen("keys.out", "w", stdout);

	n=read(); m=read();
	memset(head, -1, sizeof(head));
	for (int i=1; i<=n; ++i) t[i]=read(), c[i]=read();
	for (int i=1,u,v; i<n; ++i) {
		u=read(); v=read();
		add(u, v); add(v, u);
	}
	// force::solve();
	task::solve();

	return 0;
}
posted @   Administrator-09  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
历史上的今天:
2021-06-08 题解 随
2021-06-08 题解 big
2021-06-08 题解 string
2021-06-08 题解 题
2021-06-08 题解 [SDOI2010] 所驼门王的宝藏
2021-06-08 题解 matrix
点击右上角即可分享
微信分享提示