题解:P11812 [PA 2015] 精确打击 / Kontrmanifestacja
这个题真的好诡异啊。
谨此纪念我的第二篇黑题题解。
题目大意
题目的大概意思,没什么好解释的。
思路
考虑先把 NIE 判掉,也就是原图是个 DAG。
其次,考虑
我们继续对于
若
考虑如何处理这个东西,首先不跨环尾是好处理的,考虑用记忆化搜索处理每个点
但是我们还是没有解决如何处理跨过环尾后面一段标记的问题,既然正着不好找,不妨直接从终点
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=500010;
int n,m,state[N],fa[N],ok[N],vis[N],prex[N],mx[N],mn[N],gd[N];
vector<int> G[N],g[N],cir;
bool dfs(int u)
{
state[u]=1;
for(int v:G[u])
{
if(!state[v])
{
fa[v]=u;
if(dfs(v)) return 1;
}
else if(state[v]==1)
{
int x=u;
while(x!=v)
{
cir.push_back(x);
x=fa[x];
}
cir.push_back(v);
reverse(cir.begin(),cir.end());
return 1;
}
}
state[u]=2;
return 0;
}
bool check(int u)
{
state[u]=1;
for(int v:G[u])
{
if(ok[v]) continue;
if(state[v]==1) return 1;
if(!state[v] and check(v)) return 1;
}
state[u]=2;
return 0;
}
void del(int l,int r)
{
l=max(1LL,l);
r=min((int)cir.size(),r);
if(l>r) return;
prex[l]++;
prex[r+1]--;
}
void solve1(int u)
{
if(vis[u]) return;
vis[u]=1;
if(ok[u])
{
mx[u]=mn[u]=ok[u];
return;
}
mx[u]=0;
mn[u]=n+1;
for(int v:G[u])
{
solve1(v);
mx[u]=max(mx[u],mx[v]);
mn[u]=min(mn[u],mn[v]);
}
}
void solve2(int u)
{
if(vis[u]) return;
vis[u]=1;
if(ok[u])
{
mx[u]=ok[u];
return;
}
mx[u]=0;
for(int v:g[u])
{
solve2(v);
mx[u]=max(mx[u],mx[v]);
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
G[u].push_back(v);//正图
g[v].push_back(u);//反图
}
for(int i=1;i<=n;i++)
if(!state[i] and dfs(i)) break;
if(cir.empty())
{
cout<<"NIE\n";
return 0;
}//判 DAG
int tot=0;
for(auto v:cir)
ok[v]=++tot;
memset(state,0,sizeof(state));
for(int i=1;i<=n;i++)
if(!ok[i] and !state[i] and check(i))
{
cout<<0<<"\n";//判断又没有其他环(这里也可以写拓扑然后判断是否还有点有度数)
return 0;
}
memset(vis,0,sizeof(vis));
for(int idx:cir)
{
for(int to:G[idx])
{
solve1(to);
if(mx[to] and mx[to]>=ok[idx])
del(ok[idx]+1,mx[to]-1);
if(mn[to]!=n+1 and mn[to]<=ok[idx])
del(ok[idx]+1,cir.size());
}
}
memset(vis,0,sizeof(vis));
for(int idx:cir)
{
for(int to:g[idx])
{
solve2(to);
if(mx[to] and mx[to]>=ok[idx])
del(1,ok[idx]-1);
}
}
int k=cir.size(),ans=0;
for(int i=1;i<=k;i++)
{
prex[i]+=prex[i-1];
if(!prex[i]) gd[cir[i-1]]=1;
}
for(int i=1;i<=n;i++)
ans+=gd[i];
cout<<ans<<"\n";
for(int i=1;i<=n;i++)
if(gd[i]) cout<<i<<" ";
return 0;
}