[题解]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
原式变形可得:
前缀和优化即可,时间复杂度\(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表示。那么根据题目要求:
a在c前。a在d前。b在d前。
考虑先把a和d排好,形如:
aaaadddddd
接下来考虑放b。不难发现如果我们在a和d的交界处放\(x\)个b的话,c将有\(x+D+1\)个空隙可以选择(每个空隙可以放入任意个)。
那么答案即为:
其中\(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}\)。
带入上式得:
-
前者需要求出\(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\)次询问的答案即为:
其中前\(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;
}
浙公网安备 33010602011771号