AtCoder Beginner Contest 326 题解

发布时间 2023-10-28 22:51:00作者: Binary_1110011

首先,\(\text{Happy Birthday to me !}\)

A - 2UP3DOWN

常规ABCA...

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
using namespace std;
int n,m,a[200005],ans;
signed main(){
	IOS;TIE;
	cin>>n>>m;
	if(m>=n-3&&m<=n+2) cout<<"Yes"<<endl;
	else cout<<"No"<<endl; 
	return 0;
}

B - 326-like Numbers

范围很小,直接枚举。

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
using namespace std;
int n,m,a[200005],ans;
signed main(){
	IOS;TIE;
	cin>>n;
	for(int i=n;;i++){
		if(i/100*(i/10%10)==i%10){
			cout<<i<<endl;
			return 0;
		}
	}
	return 0;
}

C - Peak

开始把 \(M\) 范围看小了还写了个小丑做法。

排完序直接二分第一个 \(>a_i+m-1\) 的位置,算一下就好。

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
using namespace std;
int n,m,a[300005],ans;
signed main(){
	IOS;TIE;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
		ans=max(ans,upper_bound(a+i,a+n+1,a[i]+m-1)-a-i);
	}
	cout<<ans<<endl;
	return 0;
}

D - ABC Puzzle

首先这个数据范围肯定是搜索。直接搜过不去,考虑怎么剪点枝。

如果搜的过程中某行或某列出现了重复的字母,不用搜下去了(懒得记次数)。然后就是给的第一行第一列信息,如果当前是这一行或这一列的第一个填的字母,直接填给定的就行。

写了个依托答辩,怪不得 \(\text{CSPS}\)\(\%\) 你写不出来(

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
using namespace std;
int n;
string s,t;
char c[35];
int id(int x,int y){
	return (x-1)*n+y;
}
bool check(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(c[id(i,j)]!='.'){
				if(c[id(i,j)]==s[i]) break;
				else return 0;
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(c[id(j,i)]!='.'){
				if(c[id(j,i)]==t[i]) break;
				else return 0;
			}
		}
	}
	
	for(int j=1;j<=n;j++){
		int fa=0,fb=0,fc=0;
		for(int i=1;i<=n;i++){
			fa+=(c[id(j,i)]=='A');
			fb+=(c[id(j,i)]=='B');
			fc+=(c[id(j,i)]=='C');
		}
		if(fa!=1||fb!=1||fc!=1) return 0;
	}
	for(int j=1;j<=n;j++){
		int fa=0,fb=0,fc=0;
		for(int i=1;i<=n;i++){
			fa+=(c[id(i,j)]=='A');
			fb+=(c[id(i,j)]=='B');
			fc+=(c[id(i,j)]=='C');
		}
		if(fa!=1||fb!=1||fc!=1) return 0;
	}
	return 1;
}
bool ok(){
	for(int j=1;j<=n;j++){
		int fa=0,fb=0,fc=0;
		for(int i=1;i<=n;i++){
			fa+=(c[id(j,i)]=='A');
			fb+=(c[id(j,i)]=='B');
			fc+=(c[id(j,i)]=='C');
		}
		if(fa>1||fb>1||fc>1) return 0;
	}
	for(int j=1;j<=n;j++){
		int fa=0,fb=0,fc=0;
		for(int i=1;i<=n;i++){
			fa+=(c[id(i,j)]=='A');
			fb+=(c[id(i,j)]=='B');
			fc+=(c[id(i,j)]=='C');
		}
		if(fa>1||fb>1||fc>1) return 0;
	}
	return 1;
}
void dfs(int x,int y){
	if(!ok()) return ;
	if(id(x,+y)==n*n+1){
		if(check()){
			cout<<"Yes"<<endl;
			for(int i=1;i<=n;i++){
				for(int j=1;j<=n;j++) cout<<c[id(i,j)];
				cout<<endl;
			}
			exit(0);
		}
		return ;
	}
	bool fl1=1,fl2=1;
	for(int i=1;i<y;i++) if(c[id(x,i)]!='.') fl1=0;
	for(int i=1;i<x;i++) if(c[id(i,y)]!='.') fl2=0;
	int yy=y+1,xx=x;
	if(yy>n) xx++,yy=1;
	dfs(xx,yy);
	if(fl1) c[id(x,y)]=s[x],dfs(xx,yy);
	else if(fl2) c[id(x,y)]=t[y],dfs(xx,yy);
	else{
		for(char i='A';i<='C';i++){
			c[id(x,y)]=i;
			dfs(xx,yy);
		}
	}
	c[id(x,y)]='.';
}
signed main(){
	IOS;TIE;
	cin>>n>>s>>t;
	s=" "+s,t=" "+t;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) c[id(i,j)]='.';
	}
	dfs(1,1);
	cout<<"No"<<endl;
	return 0;
}

E - Revenge of "The Salary of AtCoder Inc."

比较简单的期望题。(然而一开始又看错了题/fn)

套路地,我们讨论每个数字对于最终答案的贡献。

以四个数 1 2 3 4 为例:

首先是他们第一次就被选到的概率,显然都为 \(\frac14\)

1 2 3 4
第1次 \(\frac14\) \(\frac14\) \(\frac14\) \(\frac14\)

然后考虑第二次的贡献:

  • \(1\) 不可能被选到
  • \(2\) 产生贡献前提是上一次选到了 \(1\),然后这次选到 \(2\)。故概率是 \(\frac14\times\frac14=\frac{1}{4^2}\)
  • \(3\) 产生贡献前提是上一次选到了 \(1\)\(2\),然后这次选到 \(3\)。故概率是 \((\frac14+\frac14)\times\frac14=\frac{2}{4^2}\)
  • \(4\) 产生贡献前提是上一次选到了 \(1\)\(2\)\(3\),然后这次选到 \(4\)。故概率是 \((\frac14+\frac14+\frac14)\times\frac14=\frac{3}{4^2}\)
1 2 3 4
第1次 \(\frac14\) \(\frac14\) \(\frac14\) \(\frac14\)
第2次 \(0\) \(\frac{1}{4^2}\) \(\frac{2}{4^2}\) \(\frac{3}{4^2}\)

同理考虑第三次的贡献:

  • \(1\)\(2\) 不可能被选到
  • \(3\) 产生贡献前提是上一次选到了 \(1\)\(2\),然后这次选到 \(3\)。故概率是 \((0+\frac{1}{4^2})\times\frac14=\frac{1}{4^3}\)
  • \(4\) 产生贡献前提是上一次选到了 \(1\)\(2\)\(3\),然后这次选到 \(4\)。故概率是 \((0+\frac{1}{4^2}+\frac{2}{4^2})\times\frac14=\frac{3}{4^3}\)

第四次概率也同理。

所以我们得到最终表格:

1 2 3 4
第1次 \(\frac14\) \(\frac14\) \(\frac14\) \(\frac14\)
第2次 \(0\) \(\frac{1}{4^2}\) \(\frac{2}{4^2}\) \(\frac{3}{4^2}\)
第3次 \(0\) 0 \(\frac{1}{4^3}\) \(\frac{3}{4^3}\)
第4次 \(0\) 0 0 \(\frac{1}{4^4}\)

发现了什么?第 \(j\) 次贡献的分母是 \(n^j\),而对于每个数 \(i\) 来说,其分子依次为杨辉三角的第 \(i\) 横排。所以最终所求答案即为:

\[\sum _ {i = 1} ^ n \sum _ {j = 1} ^ i \frac{C_{i-1}^{j-1}}{n^j}\times a_i \]

我们设第 \(i\) 个数的贡献为 \(f_i\),然后根据杨辉三角的优秀性质,就可以得到递推式:

\[f_i=f_{i-1}+f_{i-1}\times\frac1n \]

然后问题就解决了。

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
#define mod 998244353
using namespace std;
int n,a[300005],p,ans,f[300005];
int ksm(int x,int b){
	int ans=1;
	while(b){
		if(b&1) ans=ans*x%mod;
		x=x*x%mod,b>>=1;
	}
	return ans;
}
signed main(){
	IOS;TIE;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	int INV=ksm(n,mod-2),p=1ll;
	f[1]=INV;
	ans+=f[1]*a[1]%mod;
	for(int i=2;i<=n;i++){
		f[i]=f[i-1]+f[i-1]*INV%mod,f[i]%=mod;
		ans+=a[i]*f[i]%mod;
		ans%=mod;
	}
	cout<<ans<<endl;
	return 0;
}

F - Robot Rotation"

据说是折四半搜索,有点抽象。赛时感觉来不及写就来写题解了(

咕咕咕

G - Unlock Achievement

流!!赛时怎么没看!!!亏爆(然而赛后看了十几分钟才看懂)

建图:

首先把每个技能拆为 \(5\) 个点(\(1\sim5\) 级)。初始源点向每个技能的 \(1\) 级连 \(inf\)(本来就有)。然后源点向 \(2\sim5\) 级连 \(c_i\),每个技能除 \(5\) 级外的相邻等级再连 \(inf\),这样就可以做到流到每个技能对应等级的最大流为升到这一级的花费。

考虑组合技怎么算答案。对应每个组合技开点(以下记为 \(x\)),该组合技中,每个技能对应等级的点分别向点 \(x\)\(inf\)\(1\) 级除外),这样就可以保证到点 \(x\) 的最大流为其最小花费。最后所有组合技点向汇点连的 \(a_i\) 后求得最大流为 \(tmp\),最终答案就是 \((\sum a_i)-ans\)

为什么这是对的?因为最大流即为最小割,而我们建的图的最小割,就相当于每个点选了一个等级,使之满足部分组合技,而剩下一些组合技不要了(相当于割掉组合技点像汇点连的 \(a_i\) ),并且做到花费最少。

要时刻记得等级写的是从 0 开始还是从 1 开始,警钟敲烂(

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
#define inf 1e15
using namespace std;
int n,m,c[105],a[105],ans,f[55][55];

struct MaxFlow{
	int tot=1,head[50005],cur[50005],dep[50005];
	struct node{
		int to,nxt,f;
	}e[50005];
	void add(int u,int v,int f){
		e[++tot]={v,head[u],f},head[u]=tot;
		e[++tot]={u,head[v],0},head[v]=tot;
	}
	queue<int> q;
	int bfs(int s,int t){
		while(q.size()) q.pop();q.push(s);
		memset(dep,0,sizeof(dep));dep[s]=1;
		while(q.size()){
			int x=q.front();q.pop();
			for(int i=head[x];i;i=e[i].nxt){
				node tmp=e[i];
				if(tmp.f&&!dep[tmp.to]){
					dep[tmp.to]=dep[x]+1;
					q.push(tmp.to);
				}
			}
		}
		return dep[t];
	}
	int dfs(int s,int t,int f){
		if(s==t||!f) return f;
		int ans=0;
		for(int i=cur[s];i;i=e[i].nxt){
			node &x=e[i];cur[s]=i;
			if(x.f&&dep[x.to]==dep[s]+1){
				int tmp=dfs(x.to,t,min(f,x.f));
				x.f-=tmp,e[i^1].f+=tmp;
				ans+=tmp,f-=tmp;
				if(f<=0) break;
			}
		}
		return ans;
	}
	int dinic(int s,int t){
		int ans=0;
		while(bfs(s,t)){
			memcpy(cur,head,sizeof(head));
			ans+=dfs(s,t,inf);
		}
		return ans;
	}
}mf;
signed main(){
	IOS;TIE;
	cin>>n>>m;
	int S=n*5+m+1,T=n*5+m+2;
	for(int i=1;i<=n;i++) cin>>c[i];
	for(int i=1;i<=m;i++) cin>>a[i],ans+=a[i];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=4;j++){
			mf.add(S,(j-1)*n+i,c[i]);
			if(j<4) mf.add((j-1)*n+i,j*n+i,inf);
		}
	}
	for(int i=1;i<=m;i++){
		mf.add(n*5+i,T,a[i]);
		for(int j=1;j<=n;j++){
			cin>>f[i][j];
			if(f[i][j]>1) mf.add(n*(f[i][j]-2)+j,n*5+i,inf);
		}
	}
	cout<<ans-mf.dinic(S,T)<<endl;
	return 0;
}