AtCoder Regular Contest 150 (ARC150) - A+B+C+D 题解

A

题意

给定一个由 01? 组成的长为 n 序列,其中 ? 需要被替换为 01,询问是否有且仅有一种 ? 的替换方案使得序列中有 k1 并且这 k1 是连续的

序列总长度小于 3×105

题解

先分别统计整个序列中 01 的个数,若 1 的个数大于 k 显然无解

考虑序列上每一个长度为 k 的子区间,统计这个区间中 01 的个数,一个区间满足条件当且仅当

  • 这个区间中没有 0
  • 这个区间中 1 的个数与整个序列中 1 的个数一样

若有且仅有一个这样的区间就说明满足题意,否则不满足

动态维护区间就行,每次移动端点复杂度 Θ(1),总复杂度 Θ(n)

Code

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
char s[N];
void Main()
{
	memset(s,0,sizeof(s));
	int n,k;
	scanf("%d%d",&n,&k);
	scanf("%s",s+1);
	int zero=0,one=0;
	for(int i=1;i<=n;++i)
		if(s[i]=='0')++zero;
		else if(s[i]=='1')++one;
	if(one>k)return puts("No"),void(0);
	int now0=0,now1=0;
	for(int i=1;i<k;++i)
		if(s[i]=='0')++now0;
		else if(s[i]=='1')++now1;
	int ans=0;
	for(int ed=k;ed<=n;++ed)
	{
		if(s[ed]=='0')++now0;
		else if(s[ed]=='1')++now1;
		if(now0==0&&now1==one)++ans;
		if(s[ed-k+1]=='0')--now0;
		else if(s[ed-k+1]=='1')--now1;
	}
	return puts(ans==1?"Yes":"No"),void(0);
}
signed main()
{
    int T;
	scanf("%d",&T);
	while(T--)Main();
	return 0;
}

B

题意

T 组数据,每组数据给定正整数 A,B,请你找到一组非负整数 X,Y 使得在 B+YA+X 的整数倍的同时,最小化 X+Y

数据范围:1A,B109, 1T100

题解

AB 答案显然为 AB

B>A

方法一,那么直接从 0A 枚举 X,对于每一个 A+X 直接 Θ(1) 找到最小的满足题意的自然数 Y 即可,总复杂度 Θ(A)

方法二,那么从 1BA+1 枚举 B+YA+X 的值 t(若 t>BA+1 显然更劣),对于每一个 t 先找到最小的 Y 使得 B+Yt 的倍数,接着就能算出 A+X 的值,若算出的 A+X<A 那么说明 Y 太小了,令 X=0,再根据 t 算出新的 Y,若 A+XA 那么直接算出 X 即可,每次检验的复杂度还是 Θ(1),总复杂度 Θ(BA)

如果单纯使用一种方法肯定会超时,若 AB 那么使用第一种方法复杂度为 O(B),若 A>B 那么使用第二种方法复杂度也为 O(B),这样分类做,总复杂度就是 Θ(B)

Code

#include<bits/stdc++.h>
using namespace std;
void Main()
{
	long long a,b,x,y,t,ans=2e18;
	cin>>a>>b;
	if(a>=b)return cout<<a-b<<'\n',void(0);
	if(a<=sqrt(b))
	{
		int ax;
		for(x=0;x<=a;++x)
		{
			ax=a+x;
			if(b%ax==0)y=0;
			else y=ax-b%ax;
			ans=min(ans,x+y);
		}
	}
	else
	{
		int mn=b/a+1;
		for(t=1;t<=mn;++t)
		{
			if(b%t==0)y=0;
			else y=t-b%t;
			x=(b+y)/t-a;
			if(x<0)
			{
				x=0;
				y=a*t-b;
			}
			ans=min(ans,x+y);
		}
	}
	cout<<ans<<'\n';
}
signed main()
{
	int T;
	cin>>T;
	while(T--)Main();
	return 0;
}

C

题意

给定一个有 n 个点的无向连通图,每个点有一个点权 vi,同时给定一个长度为 k 的序列 {b},询问是否任意从节点 1 到节点 n 的简单路径都满足如下性质:

  • 把经过的所有点的权值组成一个序列,{b} 是这个序列的子序列

数据范围:2n105

题解

考虑 dp,设 fi 表示从节点 1 到节点 i 的所有简单路径上至少已经匹配了序列 {b} 的前 fi

考虑从节点 u 转移到节点 v

如果在节点 u 时已经至少匹配了 k 位那么就用 fu=k 更新 fv

如果在节点 v 时还没有匹配完 {b}k 位并且可以匹配第 fu+1 位那么就用 fu+1 更新 fv

总结成一个式子就是

fv=min{fv,fu+[fu<k][vv=bfu+1]}

类似 Dijkstra 做一遍就行

最后看终点,若 fn=k 则满足,否则不满足

Code

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=4e5+5,INF=1e9;
int n,m,k;
int head[N],nxt[M],to[M],cnt;
int a[N],b[N],d[N];
bool vis[N];
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,greater<pii>>q;
inline void create(int ff,int tt){nxt[++cnt]=head[ff],head[ff]=cnt,to[cnt]=tt;}
signed main()
{
	cin>>n>>m>>k;
	for(int i=1,t1,t2;i<=m;++i)cin>>t1>>t2,create(t1,t2),create(t2,t1);
	for(int i=1;i<=n;++i)cin>>a[i],d[i]=INF;
	for(int i=1;i<=k;++i)cin>>b[i];
	d[1]=bool(a[1]==b[1]);
	q.push(make_pair(d[1],1));
	while(q.size())
	{
		int pos=q.top().second;
		q.pop();
		if(vis[pos])continue;
		vis[pos]=1;
		for(int i=head[pos],tmp;i;i=nxt[i])
		{
			tmp=d[pos]+bool(d[pos]<k&&a[to[i]]==b[d[pos]+1]);
			if(d[to[i]]>tmp)
			{
				d[to[i]]=tmp;
				q.push(make_pair(d[to[i]],to[i]));
			}
		}
	}
	puts(d[n]==k?"Yes":"No");
	return 0;
}

D

题意

给定一棵有 n 个节点的有根树,每个点可以有黑白两种颜色,一开始所有点都是白色

一个点被定义为“好点”当且仅当从树根到它的简单路径上所有点都是黑色

现在可以进行操作,每次操作可以在树上所有不是“好点”中随机地选择一个染成黑色,询问期望要操作多少次才能让整棵树都变为黑色

数据范围:2n2×105

题解

假设第 i 个点被染色的次数是 xi,期望次数是 E[xi],根据期望的线性性,最终的答案就是 E[xi]

下面我们来单独看一个点 v,在考虑 v 被染色的期望次数时,根据期望的线性性,我们只考虑对于从根节点到 v 这条链上的操作

首先,对于这条链上的操作一定满足如下两个性质

  • 只会对这条链上的非“好点”的点染色
  • 在链上的所有点都被染色为黑色(v 变成“好点”)之前对这条链都会一直有操作

注意到这个点变为“好点”之前操作会一直进行,我们可以假设对于这条链上的“好点”也可以进行染色操作,只是我们忽略这些操作

这里可能较难理解,换句话说就是:由于我们只关心最后 v 变为“好点”之前期望选择了它多少次,只统计 xv,我们完全可以假设对于那些链上的“好点”也进行了操作,无论其他的点怎么操作都不会影响这条链的状态和最后得到的 v 的答案

CodeForces 上的一位用户 shiven 给出了类似的解释,也可以看看他的说法(传送门

上面的都明白之后,这条链上的问题就转化为了——现在有一条链上的共 k 个点,全为白色,每次在这 k 个点中随机选择一个点染色为黑色,求链上的所有点都被染为黑色时,其中的一个点被染色的期望次数

这个问题是不是就很熟悉了呢?就是洛谷的 P1291 百事世界杯之旅。假设现在一共有 i 个白色的点,有 ki 个黑色的点,那么下一次选择选中白色点的概率就是 ik,显然选中一个新的白色的点的期望次数就是 ki,这时白色点数量减少 1,再下一次选中白色点的概率就变为 i1k,以此类推,最终所有点都被选中一次的概率就是 i=1kki=ki=1k1i,那么其中的一个点(v)被染色的期望次数自然就是 i=1k1i

现在只需要预处理出 1i 的前缀和,对于每一个节点求出 E[xi] 再全部加起来就是答案了

还不理解的可以看官方题解,更加清楚

Code

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,mod=998244353;
int n,dep[N],inv[N],f[N],ans;
inline int add(int x,int y){x+=y;if(x>=mod)x-=mod;return x;}
inline void Add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
signed main()
{
	cin>>n;
	inv[1]=1;
	for(int i=2;i<=n;++i)inv[i]=mul(mod-mod/i,inv[mod%i]);
	for(int i=1;i<=n;++i)f[i]=add(f[i-1],inv[i]);
	dep[1]=1;
	ans=1;
	for(int i=2,tmp;i<=n;++i)cin>>tmp,dep[i]=dep[tmp]+1,Add(ans,f[dep[i]]);
	cout<<ans;
	return 0;
}

UPD:感谢 @Kan_kiz 对于 D 题题解的质疑,现在已经修改了题解

博客园传送门

知乎传送门

posted @   人形魔芋  阅读(385)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录导航