M. Obliviate, Then Reincarnate 题解
[ M. Obliviate, Then Reincarnate ]( Problem - M - Codeforces )

读题读不懂怎么办qwq~

题意分析
- 首先,我们要知道,房间编号是需要通过楼层确定的,即房间a编号变成 ((a mod n)+n) mod n,即将编号变成 [0,n] 中的正数。
//获取编号
void get(int x){
return (x%n+n)%n;
}
指令(a,b)表示将房间a所在楼层的人移到 房间 a+b ,建图就是将 get(a) 和 get(a+b) 连边权为b的边。
- 然后分析什么情况会使可能到达的房间编号组成无限集?
通过上面的建图后我们得到了n个点m条边的有向图,这张图意味着经过指令后可以到达的房间编号,并且每个新的房间编号是通过加权值得到的,如果出现了一个环,并且环的权值和不为0,那么我们就可以一直沿着这个环一直生成无限个数,即满足无限集条件。
- 怎么处理多次查询从x出发是否能构成无限集?
定义权值和非0的环为 “坏”环 ,反之为 “好”环。
如果某个点能够到达 “坏”环 ,那么这个点就能构成无限集,包括 “好”环 中的点也是如此。
那么我们只需要通过BFS或DFS对“坏”环跑个反向图,每次对连接的点做个标记即可。
然后查询的时候就可以O(1)查询这个点是否可以构成无限集了~
解题思路
-
先确定环,可以利用 tarjan 缩点
-
然后跑 dfs 检测非零环,注意我们可以找到每个环的一个代表点,然后以此为根dfs,每次注意检测是否在同一个环内。
检测环权值的方法利用势能标记法,即设根节点的势能为0,下一个点的势能就是加上边权,即从 x->y 的势能是h[y]=h[x]+w,当再次到达 已经到过的节点 时判断势能是否有变化, 即 是否 ( h[y]==h[x]]+w ),如果相同说明环权值为0,否则就是非零环,也就是“坏”环,那么就将此环编号标记。
-
然后我们构建DAG 的反向图,跑BFS 标记能到达“坏”环的连通分量。
代码
#include <bits/stdc++.h>
using namespace std;
//-------------------------------------------------------------------------------------------
#define int long long
#define lost_R ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define P pair<int,int>
#define lowbit(x) (x&(-x))
#define dbg1(x) cout<<"# "<<x<<endl
#define dbg2(x,y) cout<<"# "<<x<<" "<<y<<endl
#define endl '\n'
const int mod=998244353;
const int N=1e6+10;
const int INF=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
using ar3=array<int,3>;
using ar2=array<int,2>;
//--------------------------------------------------------------------------------------
int n,m,q;
vector<P> g[N];
int get(int x){
return (x%n+n)%n;
}
int dfn[N],low[N],col[N],num[N],tot,cnt;
int vis[N];
stack<int> st;
int rt[N];
//tarjan缩点
void tarjan(int x,int r){
low[x]=dfn[x]=++tot;
st.push(x);
vis[x]=1;
for(auto [y,w]:g[x]){
if(!dfn[y]){
tarjan(y,x);
low[x]=min(low[x],low[y]);
}else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
col[x]=++cnt;
num[cnt]++;
vis[x]=0;
while(st.top()!=x){
int y=st.top();
st.pop();
col[y]=cnt;
num[cnt]++;
vis[y]=0;
}
st.pop();
}
}
//势能标记法检测非零环
int flag[N],h[N],res[N];
void dfs(int x){
if(res[col[x]]) return; //如果已经标记过是"坏"环,剪枝
flag[x]=1;
for(auto [y,w]:g[x]){
if(col[y]!=col[x]) continue;
if(!flag[y]){ //没到过这个点
h[y]=h[x]+w;
dfs(y);
}else{ //到过这个点就检测势能变化
if(h[y]!=h[x]+w){
res[col[x]]=1;
return;
}
}
}
}
vector<int> ne[N];
void solve(){
cin>>n>>m>>q;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
int x=get(a);
int y=get(a+b);
g[x].push_back({y,b});
}
for(int i=0;i<n;i++){
if(!dfn[i]) tarjan(i,0);
}
for(int i=0;i<n;i++){
rt[col[i]]=i;//每个强连通分量找一个代表点作为根
}
for(int i=1;i<=cnt;i++){
dfs(rt[i]);//检测非零环
}
for(int x=0;x<n;x++){
for(auto [y,w]:g[x]){
if(col[x]==col[y]) continue;
ne[col[y]].push_back(col[x]);//构建反向DAG
}
}
queue<int> qq;
for(int i=1;i<=cnt;i++){
if(res[i]) qq.push(i);//把“坏”环的编号加进去
}
while(!qq.empty()){
int y=qq.front();
qq.pop();
for(auto x:ne[y]){
if(!res[x]){
res[x]=1;
qq.push(x);
}
}
}
while(q--){
int x;
cin>>x;
x=get(x);
if(res[col[x]]) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
signed main(){
lost_R;
// freopen("jia.in","r",stdin);
// freopen("jia.out","w",stdout);
int T=1;
//cin>>T;
for(int i=1;i<=T;i++){
solve();
}
return 0;
}

浙公网安备 33010602011771号