[题解]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'=0\):所有
-
\(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;
}
浙公网安备 33010602011771号