P10231 [COCI 2023/2024 #4] Putovanje 题解
知识点
多源 BFS,bitset。
题意分析
在一个图上,每个点有一个权值,求满足到每个点的距离都为其权值的点(权值为 \(-1\) 的点除外)。
思路分析
Subtask 1
我们可以发现,这个子任务的图一定是一个有序的链,那么转换成序列问题,直接根据坐标进行标记即可,不再赘述。
代吗时间复杂度 \(O(n)\),空间复杂度 \(O(n+m)\)。
namespace Subtask1{
int sum,ans;
int cnt[N];
bool Check(){
if(m!=n-1)return 0;
FOR(i,1,m)if(g.v[(i<<1)-1]!=g.v[i<<1]+1)return 0;
return 1;
}
signed Cmain(){
FOR(i,1,n)if(~a[i]){
++sum;
if(i-a[i]>0)++cnt[i-a[i]];
if(i+a[i]<=n&&a[i])++cnt[i+a[i]];
}
FOR(i,1,n)if(cnt[i]==sum)++ans;
wr(ans);
FOR(i,1,n)if(cnt[i]==sum)wr(i,' ');
return puts(""),0;
}
}
Subtask 2
在这个子任务中,只有第一个点有可能有非负权值,那么就变成了单源 BFS,或者当第一个点也是负权值时,那么全部直接输出即可。
时间复杂度 \(O(n+m)\),空间复杂度 \(O(n+m)\)。
namespace Subtask2{
bool vis[N];
int ans;
int dis[N];
queue<int> q;
bool Check(){
FOR(i,2,n)if(~a[i])return 0;
return 1;
}
signed Cmain(){
if(!~a[1]){
wr(n);
FOR(i,1,n)wr(i,' ');
return puts(""),0;
}
vis[1]=1,dis[1]=0,q.push(1);
while(!q.empty()){
int u=q.front();q.pop();
EDGE(g,i,u,v)if(!vis[v])dis[v]=dis[u]+1,vis[v]=1,q.push(v);
}
FOR(i,1,n)if(dis[i]==a[1])++ans;
wr(ans);
FOR(i,1,n)if(dis[i]==a[1])wr(i,' ');
return puts(""),0;
}
}
Subtask 3
注意到 \(n,m \leq 5 \times 10^3\),那么能够支持 \(O(nm)\) 这类的时间复杂度,我们对每个权值非负的点都做一次 BFS,再做上标记,最后一起验证即可。
时间复杂度 \(O(nm)\),空间复杂度 \(O(n+m)\)。
namespace Subtask3{
bool mark[N],vis[N];
int ans;
int dis[N];
queue<int> q;
bool Check(){
return n<=5000&&m<=5000;
}
void Bfs(int s){
RCL(vis,0,bool,n+5),vis[s]=1,dis[s]=0,q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
EDGE(g,i,u,v)if(!vis[v])vis[v]=1,dis[v]=dis[u]+1,q.push(v);
}
FOR(i,1,n)if(dis[i]!=a[s])mark[i]=1;
}
signed Cmain(){
FOR(i,1,n)if(~a[i])Bfs(i);
FOR(i,1,n)if(!mark[i])++ans;
wr(ans);
FOR(i,1,n)if(!mark[i])wr(i,' ');
return puts(""),0;
}
}
Subtask 4
观察范围 \(n \leq 5\times 10^4\),通过它来推测方案,我们可以想到优化上面的方法,把所有 BFS 放到一起做,变成多源 BFS,然后用一个很好用的 STL:bitset,它的原理是把 bool 的 1 字节缩到 1 bit,因此速度、空间都是一般 int 的 32 倍。
为保证每一个 BFS 的正确性,我们不能直接每个一起做,那怎么办呢?
观察题目,我们可以把原本的 \(dis\) 数组的定义改掉,原本是到源点的最短距离,现在改为到目标点(酒店)的距离,这样就对每一个点都适用,然后我们走一步的距离是 \(-1\),所以只要我们按点权从大到小一个个加入源点,就能保证每个点在 BFS 时的阶段相同。在 BFS 时,还要注意判断不合法情况。
所以其实方法并没有很大的提升,只是对暴力的优化。
时间复杂度 \(O(\frac{nm}{w})\),空间复杂度 \(O(\frac{nm}{w})\),其中 \(w\) 约为 \(32\)。
namespace Subtask4{
int tot,ans;
int dis[N];
bool flag=1;
bool mark[N];
queue<int> q;
bitset<N> vis[N];
vector<int> vec[N];
void Bfs(){
int cur=n;
while(cur>=0&&vec[cur].empty())--cur;
for(int i:vec[cur])dis[i]=cur,mark[i]=1,vis[i][i]=1,q.push(i);
for(int u=q.front();!q.empty();q.pop(),u=q.empty()?u:q.front())if(dis[u]){
while(!vec[dis[u]-1].empty()){
int v=vec[dis[u]-1].back();vec[dis[u]-1].pop_back();
if(mark[v]){
if(dis[v]!=a[v])flag=0;
continue;
}
mark[v]=1,dis[v]=a[v],q.push(v),vis[v][v]=1;
}
EDGE(g,i,u,v)if(dis[v]<dis[u]){
dis[v]=dis[u]-1,vis[v]|=vis[u];
if(!mark[v])q.push(v);
mark[v]=1;
}
}
}
signed Cmain(){
FOR(i,1,n)if(~a[i])vec[a[i]].push_back(i),++tot;
if(!tot){
wr(n);
FOR(i,1,n)wr(i,' ');
return puts(""),0;
}
Bfs();
if(!flag)return (cout<<0<<endl),0;
FOR(i,1,n)if(!dis[i]&&vis[i].count()==tot)++ans;
wr(ans);
FOR(i,1,n)if(!dis[i]&&vis[i].count()==tot)wr(i,' ');
return puts(""),0;
}
}
完整CODE
#include<bits/stdc++.h>
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)>(b)?(b):(a))
#define tomax(a,b) ((a)=max((a),(b)))
#define tomin(a,b) ((a)=min((a),(b)))
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define EDGE(g,i,u,x) for(register int (i)=(g).h[(u)],(x)=(g).v[(i)];(i);(i)=(g).nxt[(i)],(x)=(g).v[(i)])
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
constexpr int N=5e4+10,M=1e5+10;
int n,m;
int a[N];
struct CFS{
int tot,v[M<<1],nxt[M<<1],h[N];
void att(int U,int V){
v[++tot]=V,nxt[tot]=h[U],h[U]=tot;
}
void con(int U,int V){
att(U,V),att(V,U);
}
}g;
namespace Subtask1{
int sum,ans;
int cnt[N];
bool Check(){
if(m!=n-1)return 0;
FOR(i,1,m)if(g.v[(i<<1)-1]!=g.v[i<<1]+1)return 0;
return 1;
}
signed Cmain(){
FOR(i,1,n)if(~a[i]){
++sum;
if(i-a[i]>0)++cnt[i-a[i]];
if(i+a[i]<=n&&a[i])++cnt[i+a[i]];
}
FOR(i,1,n)if(cnt[i]==sum)++ans;
wr(ans);
FOR(i,1,n)if(cnt[i]==sum)wr(i,' ');
return puts(""),0;
}
}
namespace Subtask2{
bool vis[N];
int ans;
int dis[N];
queue<int> q;
bool Check(){
FOR(i,2,n)if(~a[i])return 0;
return 1;
}
signed Cmain(){
if(!~a[1]){
wr(n);
FOR(i,1,n)wr(i,' ');
return puts(""),0;
}
vis[1]=1,dis[1]=0,q.push(1);
while(!q.empty()){
int u=q.front();q.pop();
EDGE(g,i,u,v)if(!vis[v])dis[v]=dis[u]+1,vis[v]=1,q.push(v);
}
FOR(i,1,n)if(dis[i]==a[1])++ans;
wr(ans);
FOR(i,1,n)if(dis[i]==a[1])wr(i,' ');
return puts(""),0;
}
}
namespace Subtask3{
bool mark[N],vis[N];
int ans;
int dis[N];
queue<int> q;
bool Check(){
return n<=5000&&m<=5000;
}
void Bfs(int s){
RCL(vis,0,bool,n+5),vis[s]=1,dis[s]=0,q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
EDGE(g,i,u,v)if(!vis[v])vis[v]=1,dis[v]=dis[u]+1,q.push(v);
}
FOR(i,1,n)if(dis[i]!=a[s])mark[i]=1;
}
signed Cmain(){
FOR(i,1,n)if(~a[i])Bfs(i);
FOR(i,1,n)if(!mark[i])++ans;
wr(ans);
FOR(i,1,n)if(!mark[i])wr(i,' ');
return puts(""),0;
}
}
namespace Subtask4{
int tot,ans;
int dis[N];
bool flag=1;
bool mark[N];
queue<int> q;
bitset<N> vis[N];
vector<int> vec[N];
void Bfs(){
int cur=n;
while(cur>=0&&vec[cur].empty())--cur;
for(int i:vec[cur])dis[i]=cur,mark[i]=1,vis[i][i]=1,q.push(i);
for(int u=q.front();!q.empty();q.pop(),u=q.empty()?u:q.front())if(dis[u]){
while(!vec[dis[u]-1].empty()){
int v=vec[dis[u]-1].back();vec[dis[u]-1].pop_back();
if(mark[v]){
if(dis[v]!=a[v])flag=0;
continue;
}
mark[v]=1,dis[v]=a[v],q.push(v),vis[v][v]=1;
}
EDGE(g,i,u,v)if(dis[v]<dis[u]){
dis[v]=dis[u]-1,vis[v]|=vis[u];
if(!mark[v])q.push(v);
mark[v]=1;
}
}
}
signed Cmain(){
FOR(i,1,n)if(~a[i])vec[a[i]].push_back(i),++tot;
if(!tot){
wr(n);
FOR(i,1,n)wr(i,' ');
return puts(""),0;
}
Bfs();
if(!flag)return (cout<<0<<endl),0;
FOR(i,1,n)if(!dis[i]&&vis[i].count()==tot)++ans;
wr(ans);
FOR(i,1,n)if(!dis[i]&&vis[i].count()==tot)wr(i,' ');
return puts(""),0;
}
}
signed main(){
rd(n),rd(m);
FOR(i,1,m){
int u,v;
rd(u),rd(v),g.con(u,v);
}
FOR(i,1,n)rd(a[i]);
return Subtask4::Cmain();
}
/*
我们为每个点设一个 dis[] 表示它到酒店的距离,
然后根据 d[] 从大到小做多源 BFS,
并记录能够到达该点的合法点集.
在 BFS 的过程中,要注意判断不合法的情况:当判断出酒店到达某个点 x 的最短距离小于 d[x] 那么它就不合法.
最后判断酒店的条件就是 dis[x]==0&&vis[x].count()==tot,其中 tot 表示 d[] 不为 -1 的点数.
*/
(此处省略快读快写。)
标签:P10231,return,cur,int,题解,vis,vec,2023,dis From: https://www.cnblogs.com/Cat-litter/p/18188149