CodeQUEEN 2026 -qual- (AtCoder Beginner Contest 462)题解

一、前言

第六次 AK,记录一下。

二、正文

第 A 题 Secret Numbers

对新手很有意义的题目。

关键点在于如何判断一个字符是否为数字,我们发现数字的 ASCII 编码都是连续的,所以说 ASCII 码在 0 0 0 的 ASCII 码与 9 9 9 的 ASCII 码之间的字符都为数字。

接下来就是枚举每个字符了,本代码使用 C++14 的 auto 枚举方式。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
signed main(){
	string s; cin>>s;
	for (auto x:s){
		if (x>='0'&&x<='9') cout<<x;
	}	
}

第 B 题 Gift

简单的题目,考察 vector 的使用。

维护 n n n 个 vector,对于某个人 i i i,他给 j j j 礼物就相当于将 i i i 插入第 j j j 个 vector。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
vector <int> vc[2000010];
signed main(){
	int n; cin>>n;
	for (int i=1; i<=n; i++){
		int x; cin>>x;
		for (int j=1; j<=x; j++){
			int v; cin>>v;
			vc[v].push_back(i);
		}
	}
	for (int i=1; i<=n; i++){
		cout<<vc[i].size()<<" ";
		for (auto x:vc[i]) cout<<x<<" ";
		cout<<"\n";
	}
}

第 C 题 Not Covered Points

按照横坐标从小到大排序。

如果对于当前点,如果前面有点的纵坐标小于这个点的纵坐标,那么这个点就不是合法的。

维护前缀纵坐标最小值,答案就是这个最小值的更新次数。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
pair <int,int> pr[300010];
signed main(){
	int n; cin>>n;
	for (int i=1; i<=n; i++){
		cin>>pr[i].first>>pr[i].second;
	}
	sort(pr+1,pr+n+1);
	int mn=1000000000,ans=0;
	for (int i=1; i<=n; i++){
		if (pr[i].second<=mn) ans++,mn=pr[i].second;
	}
	cout<<ans;
}

第 D 题 Accomplice

枚举案发时间 i i i。

可能参与这起案件的凶手要满足 l ≤ i l\le i l≤i 且 r ≥ i + m − 1 r\ge i+m-1 r≥i+m−1。

按照 l l l 排序,使用树状数组就可以查询这一时间的可能的凶手人数 c c c。

那么时间 i i i 对于答案的贡献为 C c 2 C_{c}^2 Cc2。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
pair <int,int> pr[300010];
int C[2000010];
void insert(int id){
	while (id<=2000000) C[id]++,id+=(id&-id);
}
int query(int id){
	int ans=0;
	while (id) ans+=C[id],id-=(id&-id);
	return ans;
}
signed main(){
	int n,m; cin>>n>>m;
	for (int i=1; i<=n; i++){
		cin>>pr[i].first>>pr[i].second;
	}
	sort(pr+1,pr+n+1);
	int id=1,ans=0;
	for (int i=1; i<=1000000; i++){
		while (id<=n&&pr[id].first<=i){
			insert(pr[id].second); id++;
		}
		int val=query(2000000)-query(i+m-1);
		ans+=val*(val-1)/2;
	}
	cout<<ans;
}

第 E 题 Alternating Costs

假设横坐标所需移动距离为 x x x,纵坐标所需移动距离为 y y y。

显然我们可以进行 2 × min ⁡ ( x , y ) 2\times \min(x,y) 2×min(x,y) 次操作,每次操作都使用 m i n ( a , b ) min(a,b) min(a,b) 的代价。

对于下面的操作来说,我们因为只能朝着一个方向移动,所以无法避免使用 a , b a,b a,b 中较大数。

当然我们的 a , b a,b a,b 如果相差很大,我们也可以绕路,用 3 × min ⁡ ( a , b ) 3\times \min(a,b) 3×min(a,b) 代替 max ⁡ ( a , b ) \max(a,b) max(a,b)。

对于 min ⁡ ( a , b ) , max ⁡ ( a , b ) \min(a,b),\max(a,b) min(a,b),max(a,b) 的出现次数,我们分讨四种情况即可。

代码中对情况进行了简化,大家可以对着代码进行理解。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
	int t; cin>>t;
	while (t--){
		int a,b,x,y; cin>>a>>b>>x>>y;
		x=abs(x); y=abs(y);
		int ans=min(x,y)*min(a,b)*2;
		int val=min(x,y); x-=val; y-=val;
		if (x>0){
			ans+=(x+1)/2*min(a,3*b)+x/2*min(b,3*a);
		}
		else{
			ans+=(y+1)/2*min(b,3*a)+y/2*min(a,3*b);
		}
		cout<<ans<<"\n";
	}
}

第 F 题 More ABC

大炮打蚊子抢到首 A。

本题存在原题:原题链接,将 MOO 改为 ABC 即可。

对于题解,去看那道题吧,大致来说就是分治+闵可夫斯基和。

这里给出代码,以供参考:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef vector <int> minkowski;
inline minkowski operator+(minkowski a,minkowski b){
	vector <int> ans;
	int ida=0,idb=0;
	while (ida!=a.size()||idb!=b.size()){
		if (ida!=a.size()&&(idb==b.size()||a[ida]>b[idb])) ans.push_back(a[ida]),ida++;
		else ans.push_back(b[idb]),idb++;
	}
	return ans;
}
inline minkowski max(minkowski a,minkowski b){
	for (int i=1; i<a.size(); i++) a[i]+=a[i-1];
	for (int i=1; i<b.size(); i++) b[i]+=b[i-1];
	int sz=max(a.size(),b.size());
	vector <int> ans;
	for (int i=0; i<sz; i++) ans.push_back(max((i<a.size()?a[i]:(int)-1e18),(i<b.size()?b[i]:(int)-1e18)));
	for (int i=sz-1; i>0; i--) ans[i]=ans[i]-ans[i-1];
	return ans; 
}
#define ls (id<<1)
#define rs (id<<1|1)
#define mid (l+r>>1)
minkowski dp[1200010][3][3];
int a[300010];
int n,k;
void solve(int id,int l,int r){
	if (l>=r){
		for (int i=0; i<k; i++) for (int j=0; j<k; j++) dp[id][i][j].clear();
		if (k<=r-l+1) dp[id][0][0].push_back(a[l]);
		return ;
	}
	solve(ls,l,mid); solve(rs,mid+1,r);
	for (int i=0; i<k; i++){
		for (int j=0; j<k; j++){
            dp[id][i][j].clear();
			if (i+j>r-l+1||i>mid-l+1||j>r-mid) continue;
			dp[id][i][j]=dp[ls][i][0]+dp[rs][0][j];
			for (int p=1; p<k; p++){
				if (i+p>mid-l+1||k-p+j>r-mid) continue;
				dp[id][i][j]=max(dp[id][i][j],dp[ls][i][p]+dp[rs][k-p][j]+(minkowski){a[mid+k-p]});
			}
		}
	}
}
int b[300010];
signed main(){
	int t; cin>>t;
	while (t--){
		string s; cin>>s;
		int n=s.size();
		int c; cin>>c;
		for (int i=0; i+2<s.size(); i++){
			if (s.substr(i,3)=="ABC") c++;
		}
		k=3;
		for (int i=1; i<=n; i++) b[i]=1;
		for (int i=k; i<=n; i++){
			a[i]=0;
			for (int j=i-k+1; j<=i; j++){
				a[i]-=b[j]*(j==i-k+1?(s[j-1]!='A'):(j==i-k+2?(s[j-1]!='B'):(s[j-1]!='C')));
			}
		}
		solve(1,1,n); 
		if (dp[1][0][0].size()<c){
			cout<<"-1\n";
			continue;
		}
		int ans=0;
		for (int i=0; i<c; i++){
			ans+=dp[1][0][0][i]; 
		}
		cout<<-ans<<"\n";
	}
		
}

第 G 题 Completely Wrong

容斥。

假设数字 i i i 在 c c c 数组中出现 a i a_i ai 次,在 k k k 数组中出现 b i b_i bi 次。

那么如果颜色 i i i 对了 j j j 次的贡献为 C ( b i , j ) × ( a i − j ) ! − 1 C(b_i,j)\times (a_i-j)!^{-1} C(bi,j)×(ai−j)!−1,最后如果 j j j 之和为 s s s,那么对答案的贡献再乘一个 ( − 1 ) s × ( n − s ) ! (-1)^s\times (n-s)! (−1)s×(n−s)!。

理解起来也不难,前半部分表示选择对的位置,后半部分应用的是一个常见公式:将 n n n 长数组染成 a 1 a_1 a1个颜色 1 1 1, a 2 a_2 a2个颜色 2 2 2......, a m a_m am 个颜色 m m m 的方案数为 n ! × ( a 1 ) ! − 1 × ( a 2 ) ! − 1 ⋯ × ( a n ) ! − 1 n!\times (a_1)!^{-1}\times (a_2)!^{-1}\dots\times (a_n)!^{-1} n!×(a1)!−1×(a2)!−1⋯×(an)!−1。

我们可以使用多项式乘法解决这个问题,把一个颜色理解为一个多项式,把它们乘起来时,只需要类似石子合并的方式乘即可,时间复杂度两只老哥,常数还可以。

代码(取到主函数部分为多项式乘法(NTT)、多项式各类函数、FWT(FMT)的全部模板,可供大家参考):

cpp 复制代码
//Timmy's poly 
//writen by @Timmylyx
//update to 7-3
//last update:2026/4/24
//too interesting
//I love poly!
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define poly vector<int> 
const int M=998244353,ima=86583718,G1=3,G2=(M+1)/3,inv2=(M+1)/2;
vector <int> inv;
//it is time to prepare
inline int Pow(int a,int n){
	//faster pow
	int ans=1;
	while (n){
		if (n&1) ans=ans*a%M;
		a=a*a%M; n>>=1;
	}
	return ans;
}
void init(int len){
	inv.resize(len+1);
	for (int i=0; i<=len; i++){
		inv[i]=Pow(i,M-2);
	}
}
//these are ins ans outs
void readin(poly &p,int n){
	while (n--){
		int x; cin>>x;
		p.push_back(x);
	}
}
void writeout(poly p){
	for (auto x:p) cout<<x<<" ";
}
//start from easy-FFT
void FFT(poly &x,int type){
	int n=x.size();
	poly r; r.resize(n);
	//resize done
	for (int i=0; i<n; i++){
		r[i]=((r[i>>1]>>1)+((i&1)*(n>>1)));
	}
	for (int i=0; i<n; i++){
		if (i<r[i]) swap(x[i],x[r[i]]);
	}
	//exchange done
	for (int now=1; now<n; now<<=1){
		int wn=Pow((type==1?G1:G2),(M-1)/(now<<1));
		for (int tmp=(now<<1),j=0; j<n; j+=tmp){
			for (int k=j,w=1; k<j+now; k++,w=w*wn%M){
				int X=x[k],Y=w*x[k+now]%M;
				x[k]=(X+Y)%M;
				x[k+now]=(X-Y+M)%M;
			}
		}
	}
}
//four operators
poly operator +(poly a,poly b){
	int len=max(a.size(),b.size());
	a.resize(len); b.resize(len);
	for (int i=0; i<len; i++) a[i]=(a[i]+b[i])%M;
	return a;
}
poly operator -(poly a,poly b){
	int len=max(a.size(),b.size());
	a.resize(len); b.resize(len);
	for (int i=0; i<len; i++) a[i]=(a[i]-b[i]+M)%M;
	return a;
}
poly operator *(poly a,poly b){
	int n=a.size(),m=b.size();
	int len=1; while (len<n+m) len*=2;
	a.resize(len); b.resize(len);
	FFT(a,1); FFT(b,1); 
	a.resize(max(a.size(),b.size()));
	b.resize(max(a.size(),b.size()));
	for (int i=0; i<a.size(); i++) a[i]=a[i]*b[i]%M;
	FFT(a,-1);  
	for (int i=0; i<a.size(); i++) a[i]=a[i]*inv[a.size()]%M;
	a.resize(n+m-1);
	return a;
}
poly get_inv(poly x){
	int m=1; poly ans; 
	ans.push_back(Pow(x[0],M-2));
	while (m<x.size()){
		m*=2;
		auto tmp=x; 
		tmp.resize(m);
		ans=ans*((poly){2}-tmp*ans);
		ans.resize(m);
	}
	ans.resize(x.size());
	return ans;
}
poly operator /(poly a,poly b){
	return a*get_inv(b);
}
//some calculus
poly get_der(poly p){
	int n=p.size();
	poly ans;
	for (int i=1; i<n; i++) ans.push_back(i*p[i]%M);
	return ans;
}
poly get_int(poly p){
	int n=p.size();
	poly ans={0};
	for (int i=0; i<n; i++){
		ans.push_back(p[i]*inv[i+1]%M);
	}
	return ans;
}
poly get_ln(poly p){
	poly ans=get_int(get_der(p)/p);
	ans.resize(p.size());
	return ans;
}
poly get_exp(poly p){
	poly ans={1};
	int m=1;
	while (m<p.size()){
		m*=2;
		ans.resize(m); //remember to resize
		poly tmp=p; tmp.resize(m);
		ans=ans*((poly){1}-get_ln(ans)+tmp);
		ans.resize(m);
	} 
	ans.resize(p.size());
	return ans;
} 
poly Pow(poly a,int n,int N,int N2){
	if (a[0]==1){
		return get_exp(get_ln(a)*((poly){n}));
	}
	if (a[0]==0){
		//delete some zeros
		int id=-1;
		for (int i=1; i<a.size(); i++){
			if (a[i]!=0){
				id=i; break;
			}
		}
		if (id==-1){
			poly ans;
			ans.resize(a.size());
			return ans;
		}
		else{
			poly b;
			for (int i=id; i<a.size(); i++) b.push_back(a[i]);
			poly val=Pow(b,n,N,N2);
			int tmp=min((int)a.size(),id*N2);
			poly ans;
			ans.resize(tmp);
			for (int i=0; i<a.size()-tmp; i++) ans.push_back(val[i]);
			return ans;
		}
	}
	int bas=a[0],bas2=Pow(bas,N);
	for (int i=0; i<a.size(); i++) a[i]=a[i]*Pow(bas,M-2)%M;
	poly ans=Pow(a,n,N,N2);
	for (int i=0; i<a.size(); i++) ans[i]=ans[i]*bas2%M;
	return ans;
}
//quadratic residue
int a,omega;
int brute_force(int n){
	for (int i=0; i<M; i++){
		if (i*i%M==n) return i;
	}
}
//complex for cipolla 
struct cipolla{
	int rel,ima;
};
cipolla operator+(cipolla a,cipolla b){
	return cipolla{(a.rel+b.rel)%M,(a.ima+b.ima)%M};
}
cipolla operator-(cipolla a,cipolla b){
	return cipolla{(a.rel-b.rel+M)%M,(a.ima-b.ima+M)%M};
}
cipolla operator*(cipolla a,cipolla b){
	return cipolla{(a.rel*b.rel+a.ima*b.ima%M*omega)%M,(a.ima*b.rel+b.ima*a.rel)%M};
}
cipolla Pow(cipolla a,int n){
	cipolla ans=cipolla{1,0};
	while (n){
		if (n&1) ans=ans*a;
		a=a*a; n>>=1;
	}
	return ans;
}
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
void cipolla_init(int n){
	while (true){
		a=rnd()%M;
		if (Pow((a*a-n+M)%M,(M-1)/2)==M-1){
			omega=(a*a-n+M)%M;
			return ;
		}
	}
}
int cipolla_work(int n){
	cipolla_init(n);
	cipolla tmp=Pow(cipolla{a,1},(M+1)/2);
	return min(tmp.rel,M-tmp.rel);
}
poly sqrt(poly p){
	poly ans={cipolla_work(p[0])};
	int m=1;
	while (m<p.size()){
		m*=2;
		auto tmp=p; tmp.resize(m);
		ans=ans-(ans*ans-tmp)/((poly){2}*ans);
		ans.resize(m);
	}
	ans.resize(p.size());
	return ans;
}
//the last operator
poly zhengchu(poly a,poly b){
	int n=a.size(),m=b.size();
	if (n<m) a.resize(m),n=m;
	int len=n-m+1;
	reverse(a.begin(),a.end()); a.resize(len);
	reverse(b.begin(),b.end()); b.resize(len);
	poly ans=a/b; ans.resize(len); reverse(ans.begin(),ans.end());
	return ans; 
}
poly operator%(poly a,poly b){
	poly ans=a-zhengchu(a,b)*b;
	ans.resize(b.size()-1);
	return ans;
}
//trigonometric function
poly get_sin(poly p){
	poly I=(poly){ima};
	poly ans=get_exp(I*p)-get_exp((poly){0}-I*p);
	ans=ans/((poly){2}*I);
	return ans;
}
poly get_cos(poly p){
	poly I=(poly){ima};
	poly ans=get_exp(I*p)+get_exp((poly){0}-I*p);
	ans=ans/((poly){2});
	return ans;
}
//linear recursion 
poly linear_recursion_pow(poly f,int n){
	poly ans=(poly){0,1},a=(poly){0,1};
	while (n){
		if (n&1) ans=ans*a%f;
		a=a*a%f; n>>=1;
	}
	return ans;
}
int linear_recursion_easy(poly f,poly a,int n){
	reverse(f.begin(),f.end());
	for (auto&x:f) x=-x;
	f.push_back(1);
	poly p=linear_recursion_pow(f,n-1);
	int ans=0;
	for (int i=0; i<a.size(); i++){
		ans=(ans+p[i]*a[i])%M;
	}
	return (ans%M+M)%M;
}
//FMT
void fill(poly &p){
	int len=1;
	while (len<p.size()) len<<=1;
	p.resize(len); 
}
void FMT_or(poly &p,int op){
	for (int w=1,len=p.size(); w<len; w<<=1){
		for (int i=1; i<len; i++){
			if (i&w) p[i]=(p[i]+op*p[i^w]+M)%M;
		}
	}
} 
void FMT_and(poly &p,int op){
	for (int w=1,len=p.size(); w<len; w<<=1){
		for (int i=0; i<len; i++){
			if ((i&w)==0) p[i]=(p[i]+op*p[i^w]+M)%M;
		}
	}
} 
poly operator|(poly a,poly b){
	FMT_or(a,1); FMT_or(b,1);
	for (int i=0; i<a.size(); i++) a[i]=a[i]*b[i]%M;
	FMT_or(a,-1); return a; 
}
poly operator&(poly a,poly b){
	FMT_and(a,1); FMT_and(b,1);
	for (int i=0; i<a.size(); i++) a[i]=a[i]*b[i]%M;
	FMT_and(a,-1); return a; 
}
//FWT
void FWT_xor(poly &a,int op){
	for (int w=1,len=a.size(); w<len; w<<=1){
		for (int l=0,r=w-1; r<len; l+=(w<<1),r+=(w<<1)){
			for (int k=l; k<=r; k++){
				a[k]=(a[k]+a[k+w])%M;
				a[k+w]=(a[k]+2*M-2*a[k+w])%M;
				a[k]=a[k]*(op==-1?inv2:1)%M;
				a[k+w]=a[k+w]*(op==-1?inv2:1)%M; 
			}
		}
	}
}
poly operator^(poly a,poly b){
	FWT_xor(a,1); FWT_xor(b,1);
	for (int i=0; i<a.size(); i++) a[i]=a[i]*b[i]%M;
	FWT_xor(a,-1); return a; 
}
//subset convolution
int pop_count(int x){
	int ans=0;
	while (x){
		ans++; x&=x-1;
	}
	return ans;
}
poly subset(poly a,poly b){
	int len=a.size(),p=log2(len);
	poly x[30],y[30],z[30],ans; ans.resize(len);
	for (int i=0; i<=p; i++) x[i].resize(len),y[i].resize(len),z[i].resize(len);
	for (int i=0; i<=p; i++){
		for (int j=0; j<len; j++){
			if (pop_count(j)==i) x[i][j]=a[j],y[i][j]=b[j];
			else x[i][j]=0,y[i][j]=0;
		}
		FMT_or(x[i],1); FMT_or(y[i],1);
	}
	for (int i=0; i<=p; i++){
		for (int j=0; j<=i; j++){
			for (int k=0; k<len; k++) z[i][k]=(z[i][k]+x[j][k]*y[i-j][k])%M;
		}
		FMT_or(z[i],-1);
		for (int k=0; k<len; k++) if (pop_count(k)==i) ans[k]=z[i][k];
	}
	return ans;
}
int cnt1[200010],cnt2[200010];
int fac[200010];
int C(int n,int m){
	return fac[n]*Pow(fac[m],M-2)%M*Pow(fac[n-m],M-2)%M;
}
poly p[200010];
priority_queue <pair<int,int>,vector <pair<int,int>>,greater<pair<int,int>>> q;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	init(5e5);
	fac[0]=1;
	for (int i=1; i<=200000; i++){
		fac[i]=fac[i-1]*i%M;
	}
	int n; cin>>n;
	for (int i=1; i<=n; i++){
		int x; cin>>x; cnt1[x]++;
	}
	for (int i=1; i<=n; i++){
		int x; cin>>x; cnt2[x]++;
	}
	for (int i=1; i<=n; i++){
		for (int j=0; j<=min(cnt1[i],cnt2[i]); j++){
			p[i].push_back(C(cnt2[i],j)*Pow(fac[cnt1[i]-j],M-2)%M);
		}
		q.push({p[i].size(),i});
	}
	while (q.size()>=2){
		auto x=q.top().second; q.pop();
		auto y=q.top().second; q.pop();
		p[x]=p[x]*p[y];
		q.push({p[x].size(),x});
		
	}
	int lst=q.top().second;
	int tmp=Pow(fac[n],M-2);
	int ans=0;
	for (int i=1; i<=n; i++) tmp=tmp*fac[cnt1[i]]%M;
	for (int i=0; i<p[lst].size(); i++){
		if (i%2==0) ans=(ans+p[lst][i]*fac[n-i])%M;
		else ans=(ans-p[lst][i]*fac[n-i]%M+M)%M; 
	}
	cout<<ans*tmp%M;
	return 0;
}