题解 [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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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