2022 年牛客多校加赛场 B 题题解

Bustling City

https://ac.nowcoder.com/acm/contest/38727/B

B Bustling City

题意:给定一个 nn 个点 nn 条边的有向图,第 ii 个点恰有一条出边指向 aia_i。在每个点上恰有一个商人,一个时刻位于点 ii 处的商人会走到 aia_i 号节点,问每个节点第一次有 kk 个商人同时在该点的时间。n,k1×106n,k \leq 1\times 10^6

解法:将图分成若干个连通块,每个连通块由一个环和树根在环上的内向树构成。首先考虑树上的点的答案(排除树根),对于节点 uu,可以根据它的最大子树深度 hh 得到一个长度为 hh 的数组 fuf_u,第 xx 位表示第 xx 时刻它现在有多少个商人。那么这个信息是可以通过子树合并得到的:fu,i=vchildufv,i1\displaystyle f_{u,i}=\sum_{v \in {\rm child}_u}f_{v,i-1}。那么就可以进行树上启发式合并,在 O(nlogn)\mathcal O(n \log n) 的时间得到每个树上节点的答案。

对于环上的节点,考虑维护一个双向环形链表,初始时刻每个点均在环上。依次枚举时刻,再遍历链表,容易计算出该时刻下环上每个点的商人个数。若从某一时刻开始,环上节点 uu 的子树内再也没有商人进入环中,则可将该点从链表中删除。这样看似暴力的做法复杂度其实仅为各个子树深度之和——每个环上节点只会被遍历它的子树深度层,因而可以 O(n)\mathcal O(n) 的计算环上节点的答案。

因而总的复杂度为 O(nlogn+n)\mathcal O(n \log n + n)

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define min(a,b) (a<b?a:b)
using namespace std;
const int N=1e6+3;
struct hh{
	int pre,nxt;
}l[N];
struct kk{
	int to,nxt;
}e[N<<1];
int n,k,to[N],vis[N],bo[N],dep[N],ans[N],val[N],p,id[N];
int buk[N],siz[N],son[N],num,fir[N];
vector<int>c,d[N];
IL int in(){
  char c;int f=1;
  while((c=getchar())<'0'||c>'9')
    if(c=='-') f=-1;
  int x=c-'0';
  while((c=getchar())>='0'&&c<='9')
    x=x*10+c-'0';
  return x*f;
}
IL void add(int x,int y){e[++num]=(kk){y,fir[x]},fir[x]=num;}
void dfs(int u){
	if(vis[u]){
		int x=u;
		do{
			bo[x]=1,c.push_back(x),x=to[x];
		}while(x^u);
		return;
	}
	vis[u]=1;dfs(to[u]);
}
void dele(int x){
	l[l[x].pre].nxt=l[x].nxt,
	l[l[x].nxt].pre=l[x].pre;
}
void dfs1(int u,int f,int id){
	dep[u]=dep[f]+1,vis[u]=1;
	if(dep[u]>d[id].size()) d[id].push_back(1);
	else if(dep[u]) ++d[id][dep[u]-1];
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(!bo[v]) dfs1(v,u,id);
}
void dfs2(int u){
	siz[u]=1;
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(!bo[v]){
			dfs2(v),siz[u]+=siz[v];
			if(siz[son[u]]<siz[v]) son[u]=v;
		}
}
void calc(int u,int rt){
	++buk[dep[u]];
	if(buk[dep[u]]>=k+1) ans[rt]=min(ans[rt],dep[u]-dep[rt]);
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt) calc(v,rt);
}
void clear(int u){
	buk[dep[u]]=0;
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt) clear(v);
}
void dfs3(int u,int op){
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(!bo[v]&&v^son[u]) dfs3(v,0);
	if(son[u]) dfs3(son[u],1),ans[u]=min(ans[u],ans[son[u]]+1);
	++buk[dep[u]];
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		if(!bo[v]&&v^son[u]) calc(v,u);
	if(!op){
		buk[dep[u]]=0;
		for(int i=fir[u],v;v=e[i].to;i=e[i].nxt) if(!bo[v]) clear(v);
	}
}
IL int mod(int x){return x>=p?x-p:x;}
void work(int st){
	c.clear();int Max=0;
	dfs(st);dep[0]=-1;p=c.size();
	for(int i=0;i<c.size();++i) val[i]=0,dfs1(c[i],0,c[i]),dfs2(c[i]),dfs3(c[i],0);
	for(int i=0;i<c.size();++i) Max=max(Max,(int)d[c[i]].size());
	for(int i=1;i<=p;++i) l[i]=(hh){i-1,i+1};l[0].nxt=1,l[p].nxt=0,l[0].pre=p;
	Max+=c.size()+2;
	for(int i=0;i<=Max;++i){
		int now=l[0].nxt;
		while(now){
			int u=c[now-1];
			if(d[u].size()>i){
				int pre=mod((now-i-1)%p+p);
				val[pre]+=d[u][i];
				if(val[pre]>=k) ans[u]=min(ans[u],i+1);
				now=l[now].nxt;
			}
			else dele(now),now=l[now].nxt;
		}
	}
	int Min=1e9,pos=0;
	for(int i=0;i<c.size();++i)
	  if(Min>ans[c[i]]) Min=ans[c[i]],pos=i;
	for(int i=mod(pos+1);i!=pos;i=mod(i+1)){
		++Min,Min=ans[c[i]]=min(ans[c[i]],Min);
	}
}
void solve(){
	n=in(),k=in();c.clear();
	for(int i=1;i<=n;++i) ans[i]=1e9,to[i]=in(),add(to[i],i);
	--k;
	for(int i=1;i<=n;++i) if(!vis[i]) work(i);
	for(int i=1;i<=n;++i)
	  if(ans[i]==1e9) printf("-1 ");
	  else printf("%d ",ans[i]);
}
int main()
{
	int T=1;
	while(T--) solve();
  return 0;
}
全部评论

相关推荐

03-12 14:52
已编辑
长沙学院 Java
点赞 评论 收藏
分享
暴杀流调参工作者:春招又试了一些岗位,现在投递很有意思,不仅要精心准备简历,投递官网还得把自己写的东西一条一条复制上去,阿里更是各个bu都有自己的官网,重复操作无数次,投完简历卡完学历了,又该写性格测评、能力测评,写完了又要写专业笔试,最近还有些公司搞了AI辅助编程笔试,有些还有AI面试,对着机器人话也听不明白录屏硬说,终于到了人工面试又要一二三四面,小组成员面主管面部门主管面hr面,次次都没出错机会,稍有不慎就是挂。 卡学历卡项目卡论文卡实习什么都卡,没有不卡的😂
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
正在热议
更多
# 春招至今,你的战绩如何? #
13285次浏览 128人参与
# AI面会问哪些问题? #
779次浏览 18人参与
# 巨人网络春招 #
11453次浏览 224人参与
# 你的实习产出是真实的还是包装的? #
2363次浏览 47人参与
# AI时代,哪个岗位还有“活路” #
2443次浏览 47人参与
# 长得好看会提高面试通过率吗? #
2105次浏览 39人参与
# MiniMax求职进展汇总 #
24550次浏览 313人参与
# 你做过最难的笔试是哪家公司 #
980次浏览 18人参与
# HR最不可信的一句话是__ #
874次浏览 31人参与
# 沪漂/北漂你觉得哪个更苦? #
859次浏览 28人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
7883次浏览 43人参与
# XX请雇我工作 #
51096次浏览 171人参与
# 简历中的项目经历要怎么写? #
310725次浏览 4245人参与
# 简历第一个项目做什么 #
31944次浏览 353人参与
# 不考虑薪资和职业,你最想做什么工作呢? #
152716次浏览 888人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
187473次浏览 1123人参与
# AI时代,哪些岗位最容易被淘汰 #
64356次浏览 855人参与
# 如果重来一次你还会读研吗 #
229933次浏览 2011人参与
# 正在春招的你,也参与了去年秋招吗? #
364010次浏览 2640人参与
# 腾讯音乐求职进展汇总 #
160790次浏览 1114人参与
# 你怎么看待AI面试 #
180504次浏览 1286人参与
# 投格力的你,拿到offer了吗? #
178015次浏览 889人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务