[题解] Atcoder Regular Contest ARC 150 A B C D 题解
求点赞/kel/kk
A - Continuous 1
对于每一个长度为k的区间,它合法当且仅当输入序列中所有出现的1都在这个区间内,所有出现的0都在这个区间外。用前缀和判断一下即可。注意题目要求的是合法区间只有一个,而不是存在就行。
时间复杂度\(O(n)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
using namespace std;
int t,n,k,sum1[300010],sum0[300010];
string s;
int main()
{
ios::sync_with_stdio(false);
cin>>t;
rep(tn,t)
{
cin>>n>>k>>s;
LL tot0=0,tot1=0;
rep(i,n)
{
sum1[i+1]=sum1[i]+(s[i]=='1' ? 1:0);
sum0[i+1]=sum0[i]+(s[i]=='0' ? 1:0);
if(s[i]=='1') ++tot1;else if(s[i]=='0') ++tot0;
}
int cc=0;
rep(i,n-k+1)
{
int c1=sum1[i+k]-sum1[i],c0=sum0[i+k]-sum0[i];
if(c1==tot1&&c0==0) ++cc;
}
cout<<(cc==1 ? "Yes":"No")<<endl;
}
return 0;
}
B - Make Divisible
令\(B+Y=k(A+X)\)。假设我们已经确定了X,那么我们应该找到最小的整数k使得\(k(A+X) \ge B\)。因为这样可以让Y最小。当X比较小的时候,比如\(\le \sqrt{1e9}\)时,这样的X是非常少的,直接枚举即可;当\(X>\sqrt{1e9}\)时,k一定\(\le \sqrt{1e9}\),那么我们干脆枚举k,此时为了让\(k(A+X) \ge B\),X会有一个下界,容易发现X取这个下界时是最优的,因为这样可以让Y最小。(注意题目要求X和Y都是非负数)
时间复杂度\(O(t\sqrt{1e9})\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
using namespace std;
LL t,a,b,B=100000;
int main()
{
cin>>t;
rep(tn,t)
{
cin>>a>>b;
LL ans=1e18;
for(LL x=0;x<=B;++x) ans=min(ans,(b+a+x-1)/(a+x)*(a+x)-b+x);
for(LL k=1;k<=B;++k)
{
LL lim=b-k*a;
if(lim<=0) lim=0;
else lim=(lim+k-1)/k;
ans=min(ans,k*a+k*lim-b+lim);
}
cout<<ans<<endl;
}
return 0;
}
C - Path and Subsequence
注意A和B数组中是会有重复的元素的。。。我一开始被这个坑了,还在写找点双什么的
题目中的合法条件等价于:图中的每个节点都有一个值,为\(A_i\),任意一条1~n的路径都会依次经过值为\(B_1 \cdots B_k\)的点。我们只要找有没有不合法的路径就行了。任意一条路径,它一定会去尽量早地匹配B中的元素(从前往后遍历路径,碰到B中的下一个元素就匹配),以尽量使自己合法。令\(dist_i\)表示从1走到i的所有路径,在尽量早地匹配B中元素的情况下,最少匹配了B中的几个元素,容易发现这样贪心是正确的。用Dijkstra的方式转移,最后看\(dist_n\)是否等于\(|B|\)即可。题目中要求路径是简单路径,但是没有关系,把有环的路径中的环去掉变成简单路径,肯定更容易不合法,所以Dijkstra是不会经过环的。
时间复杂度\(O(nlogn)\),n、m同阶所以都用n表示了。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
using namespace std;
int n,m,k,a[100010],b[100010],dist[100010];
vector <int> g[100010];
multiset <pii> q;
int main()
{
cin>>n>>m>>k;
int x,y;
rep(i,m)
{
scanf("%d%d",&x,&y);
g[x].pb(y);g[y].pb(x);
}
repn(i,n) scanf("%d",&a[i]);
rep(i,k) scanf("%d",&b[i]);
rep(i,n+3) dist[i]=1e9;dist[1]=(b[0]==a[1] ? 1:0);q.insert(mpr(dist[1],1));
while(!q.empty())
{
pii f=*q.begin();q.erase(q.begin());
rep(i,g[f.se].size())
{
int val=f.fi+(f.fi==k ? 0:(b[f.fi]==a[g[f.se][i]] ? 1:0));
if(dist[g[f.se][i]]>val)
{
dist[g[f.se][i]]=val;
q.insert(mpr(val,g[f.se][i]));
}
}
}
puts(dist[n]==k ? "Yes":"No");
return 0;
}
D - Removing Gacha
注意题目中有四种定义:黑点,白点,好点,坏点。
令从第一次达到有i个黑点的情况,进步到第一次有i+1个黑点,期望需要的步数为\(x_i\),要求的答案就是\(\sum_{i=0}^{n-1}x_i\)(不懂的感性理解一下;证明其实还是挺显然的)。令\(prob_{k,m}\)表示第一次达到k个黑点时,有m个好点的概率(\(m\le k\))。在有k个黑点和m个好点时,我们会在\(n-m\)个坏点中随机选点染黑,坏点中有\(n-k\)个白点,砸中一个就是胜利,成功晋级到k+1个黑点;每次期望砸到\(\frac{n-k}{n-m}\)个白点,所以期望砸\(\frac{n-m}{n-k}\)次。所以:
所以我们只要对每个k求出\(\sum_m (prob_{k,m}\cdot m)\)就行了,发现这个东西就是第一次有k个黑点时期望好点个数。在我们选点染黑时,是从所有坏点中均匀随机,染到坏点中的黑点是没用的,必须染到白点才能让黑点个数+1。每个白点被选到的概率也是相同的,所以我们的染色过程是:每次从所有白点中随机选一个点染黑。这个过程非常对称,所以有k个黑点时,每个点是黑色的概率相同。此时的期望好点的个数是每个点是好点的概率之和。一个深度为d(根节点深度为1)的点是好点的概率是从它到根的d个节点都是黑点的概率。显然这个概率是\(\frac{\binom{n-d}{k-d}}{\binom{n}{k}}=\frac{(n-d)!k!}{(k-d)!n!}\)。这就已经得到一个卷积的形式了,一遍NTT即可。时间复杂度\(O(nlogn)\)。
有O(n)的神奇做法,代码只有一行,但我不会。
点击查看代码
#include <bits/stdc++.h>
#include <atcoder/all>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
using namespace std;
using mint=atcoder::modint998244353;
const LL MOD=998244353;
LL qpow(LL x,LL a)
{
LL res=x,ret=1;
while(a>0)
{
if((a&1)==1) ret=ret*res%MOD;
a>>=1;
res=res*res%MOD;
}
return ret;
}
LL n,dcnt[200010],fac[200010],inv[200010];
vector <LL> g[200010];
void dfs(int pos,int d)
{
++dcnt[d];
rep(i,g[pos].size()) dfs(g[pos][i],d+1);
}
pii calc(LL val)
{
repn(i,40) repn(j,40) if((LL)i*qpow(j,MOD-2)%MOD==val) return mpr(i,j);
}
int main()
{
fac[0]=1;repn(i,200005) fac[i]=fac[i-1]*i%MOD;
rep(i,200003) inv[i]=qpow(fac[i],MOD-2);
cin>>n;
int x;
for(int i=2;i<=n;++i)
{
scanf("%d",&x);
g[x].pb(i);
}
dfs(1,1);
vector <mint> A(n+1,0),B(n+1,0),C;
repn(i,n)
{
LL bas=fac[n-i]*inv[n]%MOD;
A[i]=bas*dcnt[i]%MOD;
}
rep(i,n+1) B[i]=inv[i];
C=atcoder::convolution(A,B);
mint ans=0;
rep(i,n)
{
mint res=n*qpow(n-i,MOD-2)%MOD,sub=C[i]*fac[i]*qpow(n-i,MOD-2);
res-=sub;
ans+=res;
}
cout<<ans.val()<<endl;
return 0;
}