[题解]AtCoder Beginner Contest 405(ABC405) A~F

A - Is it rated?

照题意判定即可。

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

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int r,x;
signed main(){
	cin>>r>>x;
	if(r>=1600&&r<=2999&&x==1) cout<<"Yes";
	else if(r>=1200&&r<=2399&&x==2) cout<<"Yes";
	else cout<<"No";
	return 0;
}

B - Not All

\(3\)种解法:

  • 暴力枚举\(x\),找到最小的\(x\)使得“\(A[1\sim (n-x)]\)不包含\([1,m]\)中所有整数”即为答案。时间复杂度\(O(n^2)\)
  • 不难发现答案具有单调性,可以二分这个\(x\)。时间复杂度\(O(n\log n)\)
  • 实际上要找的就是最小的\(i\)使得\(A[1\sim i]\)中包含\([1,m]\)的所有整数。答案即为\(n-i+1\),找不到这样的\(i\)则答案为\(0\)。时间复杂度\(O(n)\)
#1 $O(n^2)$
#include<bits/stdc++.h>
#define N 110
using namespace std;
int n,m,a[N],cnt;
bitset<N> vis;
bool check(int x){
	vis=0;
	for(int i=1;i<=x;i++) vis[a[i]]=1;
	for(int i=1;i<=m;i++) if(!vis[i]) return 0;
	return 1;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=0;i<=n;i++){
		if(!check(n-i)) cout<<i,exit(0);
	}
	return 0;
}
#2 $O(n\log n)$
#include<bits/stdc++.h>
#define N 110
using namespace std;
int n,m,a[N],cnt;
bitset<N> vis;
bool check(int x){
	vis=0;
	for(int i=1;i<=x;i++) vis[a[i]]=1;
	for(int i=1;i<=m;i++) if(!vis[i]) return 0;
	return 1;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	int l=0,r=n;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(n-mid)) l=mid+1;
		else r=mid;
	}
	cout<<l;
	return 0;
}
#3 $O(n)$
#include<bits/stdc++.h>
#define N 110
using namespace std;
int n,m,a[N],cnt;
bitset<N> vis;
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]<=m&&!vis[a[i]]) vis[a[i]]=1,cnt++;
		if(cnt==m) cout<<n-i+1,exit(0);
	}
	cout<<0;
	return 0;
}

C - Sum of Product

原式变形可得:

\[\sum\limits_{i=2}^n a[i]\times (\sum\limits_{j=1}^{i-1} a[j]) \]

前缀和优化即可,时间复杂度\(O(n)\)

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

D - Escape Route

BFS即可,如果目前取出的\((x,y)\)可以到达\((x',y')\),那么\((x',y')\)这块瓷砖上方向就指向\((x,y)\)

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

点击查看代码
#include<bits/stdc++.h>
#define N 1010
#define M 1010
using namespace std;
int n,m,dx[4]{-1,0,1,0},dy[4]{0,1,0,-1};
string s[N];
queue<pair<int,int>> q;
signed main(){
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>s[i];
		for(int j=0;j<m;j++){
			if(s[i][j]=='E') q.push({i,j});
		}
	}
	string ds="v<^>";
	while(!q.empty()){
		auto t=q.front();
		q.pop();
		int x=t.first,y=t.second;
		for(int i=0;i<4;i++){
			int xx=x+dx[i],yy=y+dy[i];
			if(xx>=0&&xx<n&&yy>=0&&yy<m&&s[xx][yy]=='.'){
				s[xx][yy]=ds[i],q.push({xx,yy});
			}
		}
	}
	for(int i=0;i<n;i++) cout<<s[i]<<"\n";
	return 0;
}

E - Fruit Lineup

假设这四种水果分别用字母abcd表示。那么根据题目要求:

  • ac前。
  • ad前。
  • bd前。

考虑先把ad排好,形如:
aaaadddddd

接下来考虑放b。不难发现如果我们在ad的交界处放\(x\)b的话,c将有\(x+D+1\)个空隙可以选择(每个空隙可以放入任意个)。

那么答案即为:

\[\sum\limits_{x\in [0,B]} f(A,B-x)\times f(x+D+1,C) \]

其中\(f(m,n)\)表示\(m\)个字母放入\(n\)个空隙的方法,实际上就是“\(m\)个球放入\(n\)个盒子,球相同,盒不同,可以为空”的方案数,则\(f(m,n)=C_{n+m-1}^{n-1}\)

解释 原问题等价于“$n+m$个球放入$n$个盒子,球相同,盒不同,不能为空”。

根据插板法,相当于有\(n+m-1\)个空隙,插\(n-1\)块板,答案即为\(C_{n+m-1}^{n-1}\)

带入上式得:

\[\sum\limits_{x\in [0,B]} C_{A-1+(B-x)}^{A-1}\times C_{C+D+x}^{D+x} \]

  • 前者需要求出\(C_{A-1+0}^{A-1},C_{A-1+1}^{A-1},\dots,C_{A-1+B}^{A-1}\)

    根据组合数公式有\(C_n^m=\frac{n}{n-m}C_{n-1}^{m}\),递推即可。

  • 后者需要求出\(C_{C+D+0}^{D+0},C_{C+D+1}^{D+1},\dots,C_{C+D+B}^{D+B}\)

    根据组合数公式有\(C_n^m=\frac{n}{m}C_{n-1}^{m-1}\),递推即可。

计算除法需要用到模意义下的乘法逆元,代码使用线性递推来实现。

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

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 1000010
#define mod 998244353
using namespace std;
int a,b,c,d,ans,inv[2*N];
int f[N];  //f[i]=C(a-1+i,a-1)  f[i]=f[i-1]*(a-1+i)/i
int g[2*N];//g[i]=C(c+i,i)      g[i]=g[i-1]*(c+i)/i
signed main(){
	cin>>a>>b>>c>>d;
	f[0]=g[0]=inv[1]=1;
	for(int i=2;i<=b+d;i++) inv[i]=(-mod/i*inv[mod%i]%mod+mod)%mod;
	for(int i=1;i<=b;i++) f[i]=f[i-1]*(a-1+i)%mod*inv[i]%mod;
	for(int i=1;i<=b+d;i++) g[i]=g[i-1]*(c+i)%mod*inv[i]%mod;
	for(int x=0;x<=b;x++) (ans+=f[b-x]*g[d+x]%mod)%=mod;
	cout<<ans<<"\n";
	return 0;
}

赛时递推求\(f,g\)的思路考虑起来比较绕,其实直接递推求出阶乘逆元,然后套公式计算\(C_n^m\)就可以了。时间复杂度\(O(\log P+n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 1000010
#define tN 3000000
#define mod 998244353
using namespace std;
int a,b,c,d,ans,fac[3*N],inv[3*N];
int qp(int a,int n){
	int fac=1;
	while(n){
		if(n&1) fac=fac*a%mod;
		a=a*a%mod,n>>=1;
	}
	return fac;
}
int C(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
signed main(){
	cin>>a>>b>>c>>d;
	fac[0]=1;
	for(int i=1;i<=tN;i++) fac[i]=fac[i-1]*i%mod;
	inv[tN]=qp(fac[tN],mod-2);
	for(int i=tN;i;i--) inv[i-1]=inv[i]*i%mod;
	for(int x=0;x<=b;x++) (ans+=C(a-1+b-x,a-1)*C(c+d+x,d+x)%mod)%=mod;
	cout<<ans<<"\n";
	return 0;
}

F - Chord Crossing

思路来自此洛谷题解 by 2012_Zhang_

下文中的\(n\)均代表输入的\(n\)\(2\)倍。

对于询问\((c,d)\),我们的答案即为满足下列条件的线段的数量:

  • 该线段的一个端点在\([c,d]\)中。
  • 另一个端点在\([1,c]\)\([d,n]\)中。

定义\(f(l,x,y)\)为左端点在\([1,l]\)中,右端点在\([x,y]\)中的线段数量。

那么第\(i\)次询问的答案即为:

\[f(d_i,d_i,n)-f(c_i,d_i,n)+f(c_i,c_i,d_i) \]

其中前\(2\)项代表左端点在\([c,d]\),右端点在\([d,n]\)的答案;后\(1\)项代表左端点在\([1,c]\),右端点在\([c,d]\)的答案。

将所有要求的\(f(l,x,y)\)按左端点\(l\)从小到大排序,离线处理。

对于左端点为\(l\)\(f\)值,要在将“左端点在\([1,l]\)的线段”都统计进树状数组/线段树的情况下,查询区间和。

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

注意:\(n\)是输入的\(2\)倍,包括树状数组的大小。

点击查看代码
#include<bits/stdc++.h>
#define N 1000010
#define Q 200010
using namespace std;
int n,m,q,idx,ans[Q];
inline int lowbit(int x){return x&-x;}
struct Seg{int l,r;}s[N];
struct Que{int l,x,y,id,mul;}t[Q*3];
struct BIT{
	int s[N<<1];
	void add(int x,int k){while(x<=n) s[x]+=k,x+=lowbit(x);}
	int query(int x){int ans=0;while(x) ans+=s[x],x-=lowbit(x);return ans;}
	int query(int l,int r){return query(r)-query(l-1);}
}bit;
signed main(){
	cin>>n>>m;
	n<<=1;
	for(int i=1;i<=m;i++) cin>>s[i].l>>s[i].r;
	cin>>q;
	for(int i=1,c,d;i<=q;i++){
		cin>>c>>d;
		t[++idx]={d,d,n,i,1};
		t[++idx]={c,d,n,i,-1};
		t[++idx]={c,c,d,i,1};
	}
	sort(s+1,s+1+m,[](Seg a,Seg b){return a.l<b.l;});
	sort(t+1,t+1+idx,[](Que a,Que b){return a.l<b.l;});
	int p=0;
	for(int i=1;i<=idx;i++){
		while(p<m&&s[p+1].l<=t[i].l) bit.add(s[++p].r,1);
		ans[t[i].id]+=t[i].mul*bit.query(t[i].x,t[i].y);
	}
	for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
	return 0;
}
posted @ 2025-05-10 22:57  Sinktank  阅读(308)  评论(0)    收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2025 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.