[题解]AtCoder Beginner Contest 401(ABC401) A~F

A - Status Code

如果\(S\in [200,299]\)则输出Success,否则输出Failure

时间复杂度\(O(1)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
signed main(){
	cin>>n;
	if(n>=200&&n<=299) cout<<"Success";
	else cout<<"Failure";
	return 0;
}

B - Unauthorized

有且只有未登录状态下的private会累加答案,模拟即可。

时间复杂度\(O(n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,in,ans;
string s;
signed main(){
	cin>>n;
	while(n--){
		cin>>s;
		if(s=="login") in=1;
		else if(s=="logout") in=0;
		else if(s=="private"&&!in) ans++;
	}
	cout<<ans<<"\n";
	return 0;
}

C - K-bonacci

快速求前\(k\)项的和,使用前缀和优化一下即可。

需要注意减法取模后可能出现负数,需要处理一下。

时间复杂度\(O(n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 1000010
#define mod 1000000000
using namespace std;
int n,k,a[N];
signed main(){
	cin>>n>>k;
	for(int i=1;i<=k;i++) a[i]=a[i-1]+1;
	for(int i=k+1;i<=n+1;i++) a[i]=(2*a[i-1]-a[i-k-1])%mod;
	cout<<(a[n+1]-a[n]+2*mod)%mod;
	return 0;
}

D - Logical Filling

题目已经保证\(X\)不为空,所以接下来都在有解的情况下讨论。

为了满足“o不相邻”,我们必须将\(S\)中所有o的左右都置为.,相应地\(k\)的值也会发生变化。

这样,我们仅需在这个新字符串\(S'\)的基础上考虑如何满足“恰有\(k'\)o”即可。

\(S'\)中连续的?构成了若干个区间,我们令这些区间的长度分别为\(a_1,a_2,\dots,a_m\)

接下来我们要将\(k'\)o分配到这些区间内,然而一个区间所能容纳的o的个数是有限的。由于要求不能有相邻的o,所以不难发现第\(i\)个区间最多容纳\(\lceil\frac{a_i}{2}\rceil\)o

那么这些区间一共最多容纳\(cnt=\sum\limits_{i=1}^{m}\lceil\frac{a_i}{2}\rceil\)o

由于保证一定有解,所以\(k'\le cnt\),进一步讨论。

  • \(k'<cnt\)

    • \(k'=0\):所有?处只能填.
    • \(k'>0\)\(k'<cnt\)说明一定至少有一个区间没装满,而\(k'>0\)又说明至少有一个区间非空,那么任何一个?处,既有可能取到o,又有可能取到.
      所以没有一个位置是确定的,因此所有?全部原样输出。
  • \(k'=cnt\):此时所有区间都是满的,对于一个区间:

    • 如果其长度为奇数,那么该区间的答案是唯一的,形如o.o.o.o,因此直接输出它。
    • 如果其长度为偶数,那么该区间有\(2\)种可能的填法,形如o.o.o..o.o.o,因此全部输出?

    其他位置原样输出即可。

时间复杂度\(O(n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,k,cnt;
string s;
signed main(){
	cin>>n>>k>>s,s='#'+s+'#';
	for(int i=1;i<=n;i++) if(s[i]=='o') k--,s[i-1]=s[i+1]='.';
	for(int i=1,l=0;i<=n;i++){
		if(s[i]=='?'&&s[i-1]!='?') l=i;
		if(s[i]=='?'&&s[i+1]!='?') cnt+=(i-l)/2+1;
	}
	if(!k) for(int i=1;i<=n;i++) cout<<(s[i]=='?'?'.':s[i]);
	else if(cnt!=k) for(int i=1;i<=n;i++) cout<<s[i];
	else{
		for(int i=1,l=0;i<=n;i++){
			if(s[i]!='?') cout<<s[i];
			else{
				if(s[i-1]!='?') l=i;
				if(s[i+1]!='?'){
					if((i-l)&1) for(int j=l;j<=i;j++) cout<<'?';
					else for(int j=l;j<=i;j++) cout<<(((j-l)&1)?'.':'o');
				}
			}
		}
	}
	return 0;
}

E - Reachable Set

从小到大遍历每一个点,对于点\(u\)

  • 遍历\(u\)的邻接点\(v\),对于\(v<u\),用并查集将\(u\)\(v\)合并成一个连通块。
  • 合法性判断:要看\(1,2,\dots,u\)是否互相连通,仅需看此时这些节点所组成的连通块个数是不是\(1\)即可。可以用并查集来维护连通块数量\(cnt\)
  • 计算答案:即\(1,2,\dots u\)构成的连通块的外围节点个数,可以在维护连通性的过程中,用一个数组来实时标记每个节点是否是外围节点,并用\(ans\)来记录外围节点个数。

时间复杂度\(O(n\alpha(n))\)

点击查看代码
#include<bits/stdc++.h>
#define N 200010
using namespace std;
int n,m,fa[N],ans,cnt;
bitset<N> mark;
vector<int> G[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge(int x,int y){
	x=find(x),y=find(y);
	if(fa[x]!=y) cnt--,fa[x]=y;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		G[u].emplace_back(v),G[v].emplace_back(u);
	}
	for(int i=1;i<=n;i++){
		cnt++;
		if(mark[i]) mark[i]=0,ans--;
		for(int j:G[i]){
			if(j<i) merge(i,j);
			else if(!mark[j]) mark[j]=1,ans++;
		}
		cout<<(cnt==1?ans:-1)<<"\n";
	}
	return 0;
}

F - Add One Edge 3

考虑在\(G_1\)\(u\)\(G_2\)\(v\)之间加了一条边,则新树的直径可能是:

  • \(G_1\)的直径。
  • \(G_2\)的直径。
  • \(G_1\)中从\(u\)开始的最长路径\(+G_2\)中从\(v\)开始的最长路径\(+1\)

假设我们已经计算出了\(D,f_1[u],f_2[u]\),其中:

  • \(D\)表示\(G_1,G_2\)直径的最大值。
  • \(f_1[u]\)表示\(G_1\)中从\(u\)开始的最长路径。
  • \(f_2[u]\)表示\(G_2\)中从\(u\)开始的最长路径。

那么答案即为\(\sum\limits_{i\in[1,n_1],j\in[1,n_2]} \max(f_1[i]+f_2[j]+1,D)\)

枚举\(i,j\)不可行,考虑将\(f_1,f_2\)排序,然后仅枚举\(i\)

由于\(f_2\)单调,所以一定存在一个分割点\(j\),使得:

  • 对于所有\(k\in[1,j]\)\(\max\)都取右边;
  • 对于所有\(k\in (j,n_2]\)\(\max\)都取左边。

又由于\(f_1\)单调,所以随着\(i\)的单增,\(j\)一定是单调不降的,类似滑动窗口。

对于每个\(i\),累加上面两种情况的贡献即可,需要用到前缀和。


至于\(f_1,f_2\)如何计算,有一个比较显然的结论:\(f[u]=\max(d(u,x),d(u,y))\),其中\(dis(a,b)\)表示\(a,b\)的距离,\(x,y\)是直径的两点。可以用反证法证明。

有了这个结论,我们就可以在\(2\)次DFS求直径的过程中,以\(x\)为起点时更新一次\(f\),再额外以\(y\)为起点再更新一次\(f\),这样就计算出来了。


时间复杂度\(O(n\log n)\),瓶颈在于排序。

点击查看代码
#include<bits/stdc++.h>
#define N 200010
#define int long long
#define eb emplace_back
using namespace std;
struct Tree{
	int n,f[N],d[N]{-1},c,D,ds[N];
	vector<int> G[N];
	void add(int u,int v){G[u].eb(v),G[v].eb(u);}
	void dfs(int u,int fa){
		d[u]=d[fa]+1,f[u]=max(f[u],d[u]);
		if(d[u]>d[c]) c=u;
		for(int i:G[u]) if(i!=fa) dfs(i,u);
	}
	void init(){
		cin>>n;
		for(int i=1,u,v;i<n;i++) cin>>u>>v,G[u].eb(v),G[v].eb(u);
		dfs(1,0),dfs(c,0),dfs(c,0),D=f[c],sort(f+1,f+1+n);
		for(int i=n;i>=1;i--) ds[i]=ds[i+1]+f[i];
	}
}t[2];
signed main(){
	t[0].init(),t[1].init();
	int D=max(t[0].D,t[1].D);
	int p=t[1].n,ans=0;
	for(int i=1;i<=t[0].n;i++){
		while(p&&t[0].f[i]+t[1].f[p]+1>D) p--;
		ans+=p*D+(t[1].n-p)*(t[0].f[i]+1)+t[1].ds[p+1];
	}
	cout<<ans<<"\n";
	return 0;
}
posted @ 2025-04-12 23:25  Sinktank  阅读(543)  评论(1)    收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2025 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.