[题解]AtCoder Beginner Contest 425(ABC425) A~F

A - Sigma Cubes

\((-1)^i\)\(i\) 为奇数时为 \(-1\),偶数时为 \(1\)。按题意模拟即可。

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

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,ans;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		if(i&1) ans-=i*i*i;
		else ans+=i*i*i;
	}
	cout<<ans<<"\n";
	return 0;
}

B - Find Permutation 2

初始定义集合 \(S=\{1,2,\dots,n\}\)

\(A\) 中非 \(-1\) 的值从 \(S\) 中删掉,剩下的元素按任意顺序填入 \(-1\) 处即可。

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

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=15;
int n,a[N];
set<int> s;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) s.insert(i);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(~a[i]){
			if(s.count(a[i])){
				s.erase(a[i]);
			}else{
				cout<<"No\n";
				return 0;
			}
		}
	}
	cout<<"Yes\n";
	for(int i=1;i<=n;i++){
		if(~a[i]) cout<<a[i]<<" ";
		else{
			cout<<*(s.begin())<<" ";
			s.erase(s.begin());
		}
	}
	return 0;
}

C - Rotate and Sum Query

下文规定下标从 \(0\) 开始。

容易发现,若旋转操作总共进行了 \(k\) 次,则 \([l,r]\) 的查询相当于原数组中 \([(l+k)\bmod n,(r+k)\bmod n]=[L,R]\) 的区间和。

特别地,若 \(L>R\),意味着实际上答案是 \([L,N]+[0,R]\)

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

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,q,a[N],o;
inline int sum(int l,int r){
	if(!l) return a[r];
	return a[r]-a[l-1];
}
signed main(){
	cin>>n>>q;
	for(int i=0;i<n;i++){
		cin>>a[i];
		if(i) a[i]+=a[i-1];
	}
	int op,x,y;
	while(q--){
		cin>>op;
		if(op==1){
			cin>>x;
			o+=x;
		}else{
			cin>>x>>y;
			x+=n-1,y+=n-1;
			x=(x+o)%n;
			y=(y+o)%n;
			if(x<=y){
				cout<<sum(x,y)<<"\n";
			}else cout<<sum(x,n-1)+sum(0,y)<<"\n";
		}
	}
	return 0;
}

也可以用官方题解的方法,将原数组复制一份放在后面,避免拆段。

D - Ulam-Warburton Automaton

BFS 即可。

需要注意的是,每一轮的所有变化是同时进行的。所以不能搜到一个需要入队元素就将它的访问情况设为 #,而是应该将当前轮所有需要入队的元素存起来,等该轮结束后一并设为 #

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

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,m,dx[4]{-1,0,1,0},dy[4]{0,1,0,-1},ans,pt;
struct Node{int tim,x,y;};
queue<Node> q;
string s[N];
inline int deg(int x,int y){
	return (s[x+1][y]=='#')+(s[x-1][y]=='#')+(s[x][y+1]=='#')+(s[x][y-1]=='#');
}
vector<Node> tmp;
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>s[i],s[i]=' '+s[i]+' ';
		for(int j=1;j<=m;j++){
			if(s[i][j]=='#'){
				q.push({0,i,j});
			}
		}
	}
	s[0].resize(m+2,' ');
	s[n+1].resize(m+2,' ');
	while(!q.empty()){
		tmp.clear();
		while(!q.empty()){
			Node t=q.front();
			if(t.tim!=pt){
				pt=t.tim;
				break;
			}
			q.pop();
			int x=t.x,y=t.y,tim=t.tim,xx,yy;
			for(int i=0;i<4;i++){
				xx=x+dx[i],yy=y+dy[i];
				if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&s[xx][yy]!='#'&&deg(xx,yy)==1){
					tmp.emplace_back(Node{tim+1,xx,yy});
				}
			}
		}
		for(Node i:tmp){
			s[i.x][i.y]='#';
			q.push(i);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			ans+=(s[i][j]=='#');
		}
	}
	cout<<ans<<"\n";
	return 0;
}

E - Count Sequences 2

多重集合排列板子,答案即为:

\[\frac{(C_1+C_2+\dots+C_n)!}{C_1!C_2!\dots C_n!} \]

但我们发现,此题中的模数并非质数,所以可能找不到乘法拟元。

考虑对 \(C\) 进行质因数分解,从而进行约分。

具体来说,在欧拉筛的过程中记录下 \(i\) 的最小质因数 \(mn_i\)。从大到小遍历每一个 \(i\),将它的指数分配给 \(mn_i\)\(\frac{i}{mn_i}\),直到只有质数剩余即可。

时间复杂度 \(O(T\sum C_i)\approx 5\times 10^8\),不过跑不满,可以通过。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5,C=5e3+5;
int n,p[N],mn[N],idx,t,m,a[C],d[C],c[N];
int qp(int a,int n){
	int f=1;
	while(n){
		if(n&1) f=f*a%m;
		a=a*a%m,n>>=1;
	}
	return f;
}
void init(int n){
	for(int i=2;i<=n;i++){
		if(!mn[i]) p[++idx]=mn[i]=i;
		for(int j=1;j<=idx&&p[j]*i<=n;j++){
			mn[i*p[j]]=p[j];
			if(i%p[j]==0) break;
		}
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>t>>m;
	init(N-1);
	while(t--){
		cin>>n;
		int sum=0,ans=1;
		for(int i=1;i<=n;i++) cin>>c[i],sum+=c[i];
		d[sum+1]=0;for(int i=2;i<sum;i++) d[i]=0;
		d[sum]=1;//分子 
		for(int i=1;i<=n;i++) d[c[i]]--;//分母
		for(int i=sum;i>1;i--) d[i]+=d[i+1];//后缀和还原回原指数
		for(int i=sum;i>1;i--){//约分
			if(mn[i]!=i) d[mn[i]]+=d[i],d[i/mn[i]]+=d[i];
		}
		for(int i=sum;i>1;i--){
			if(mn[i]==i) ans=ans*qp(i,d[i])%m;
		}
		cout<<ans<<"\n"; 
	}
	return 0;
}

官方题解给的思路是将上式进行拆分:

\[\frac{(C_1+C_2+\dots+C_n)!}{C_1!C_2!\dots C_n!}=\binom{C_1}{C_1}\binom{C_1+C_2}{C_2}\dots\binom{C_1+C_2+\dots+C_n}{C_n} \]

通过 \(O((\sum C_i)^2)\) 的递推预处理组合数,可以做到单个样例 \(O(n)\)

时间复杂度 \(O((\sum C_i)^2+\sum n)\approx 2.5\times 10^7\)

F - Inserting Process

\(n\) 只有 \(22\),考虑状压 DP。

每种可能出现的字符串,都可以表示为一个长度为 \(n\) 的二进制状态。其中,第 \(i\) 位为 \(1\) 表示 \(s_i\) 未被删除。

\(f[2^n-1]=1\) 为初状态,\(f[0]\) 为答案。

转移时,需要注意 \(s\) 中连续相同段的问题。比如 aaa 这个串,011101 两个状态本质是相同的。

所以我们额外规定,删除字符时,只能选择当前段最左侧尚未被删除的字符。

时间复杂度 \(O(2^n\times n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=22,P=998244353;
int f[1<<N],n,t;
string s;
signed main(){
	cin>>n>>s;
	f[t=(1<<n)-1]=1;
	for(int i=t;~i;i--){
		int pre=-1;
		for(int j=0;j<n;j++){
			if((i>>j)&1){
				if(s[j]!=pre){
					(f[i^(1<<j)]+=f[i])%=P;
					pre=s[j];
				}
			}
		}
	}
	cout<<f[0]<<"\n";
	return 0;
}
posted @ 2025-09-29 21:53  Sinktank  阅读(62)  评论(0)    收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2025 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.