题解:P17195 [KOI 2026 #2] 游戏
lailai0916 · · 题解
题意简述
给定一张无向图。 一条边的权值表示两端房间间的通道数量。
每局游戏给定起点
询问 Alice 能否保证到达某个出口。
解题思路
先固定参数
初始时,所有出口都属于
Alice 只需选择其中任意
不断执行这条规则,直到无法继续。
下面证明最终的
对集合外的每个房间,
通向
否则,Alice 选出的
因此,上述闭包恰好等于必胜房间集合。
不同询问的闭包具有单调性。
参数越小,加入一个房间的条件越宽松。
所以,按
维护当前已经加入闭包的房间。
对每个尚未加入的房间
其中
房间
使用大根堆维护最大的
下面说明降序处理始终得到正确闭包。
处理当前参数
随后不断加入满足
所以,此时的集合既不会加入错误房间,
也已经达到参数
每条无向边在某个端点加入时,
至多为另一个端点产生一次有效更新。
堆操作总数为
预处理与回答询问的总时间复杂度为:
空间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=200005;
const int M=800005;
struct Query
{
int s,id;
ll k;
}ask[N];
int head[N],to[M],nxt[M],w[M],ec;
ll sum[N];
bool win[N],ans[N];
void add(int u,int v,int z)
{
to[++ec]=v;
w[ec]=z;
nxt[ec]=head[u];
head[u]=ec;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,q;
cin>>n>>m>>q;
for(int i=1;i<=n;i++)cin>>win[i];
for(int i=1;i<=m;i++)
{
int u,v,z;
cin>>u>>v>>z;
add(u,v,z);
add(v,u,z);
}
for(int i=1;i<=q;i++)
{
cin>>ask[i].s>>ask[i].k;
ask[i].id=i;
}
sort(ask+1,ask+q+1,[](const Query &x,const Query &y){return x.k>y.k;});
priority_queue<pair<ll,int>> h;
for(int i=1;i<=n;i++)if(win[i])
{
for(int j=head[i];j;j=nxt[j])if(!win[to[j]])sum[to[j]]+=w[j];
}
for(int i=1;i<=n;i++)if(!win[i])h.push({sum[i],i});
for(int i=1;i<=q;i++)
{
ll k=ask[i].k;
while(1)
{
while(!h.empty()&&(win[h.top().second]||h.top().first!=sum[h.top().second]))h.pop();
if(h.empty()||h.top().first<k)break;
int u=h.top().second;
h.pop();
win[u]=1;
for(int j=head[u];j;j=nxt[j])
{
int v=to[j];
if(win[v])continue;
sum[v]+=w[j];
h.push({sum[v],v});
}
}
ans[ask[i].id]=win[ask[i].s];
}
for(int i=1;i<=q;i++)cout<<(ans[i]?"YES":"NO")<<'\n';
return 0;
}