CF Round 1004 题解合集
感觉是 good round。
2C
唯一没做明白的题。
经过若干手玩,发现在最优次数内达到目标,加的值是固定的。
也就是说,如果我加了 \(9\),想要达到目标,以后不可能再加 \(99\)。
又因为感觉答案上界很小,所以直接暴搜即可。
复杂度 \(O(能过)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e9;
const int a[10]={9,99,999,9999,99999,999999,9999999,99999999,999999999,9999999999};
int t,n,ans;
bool check(int n){
while(n){
if(n%10==7) return true;
n/=10;
}
return false;
}
void dfs(int step,int n,int i){
if(step>=ans) return;
if(check(n)){
ans=min(ans,step);
return;
}
dfs(step+1,n+a[i],i);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
ans=inf;
for(int i=0;i<=9;i++){
dfs(0,n,i);
}
cout<<ans<<'\n';
}
return 0;
}
2D & 1A
趣味交互题。
首先,如果 \(x\) 不是一个排列,那么我们询问不在 \(x\) 中的数和任意数,返回 \(0\) 确定是 \(A\),否则确定是 \(B\);
然后,考虑 \(x\) 是排列的情况。此时如果是 \(A\),那么图一定是一棵内向基环树森林。
考虑图上两个点如果相互可达且距离相等,那么它们一定在同一个环上,并且距离的上界是 \(\lfloor \frac{n}{2} \rfloor\)。
所以我们选择 \(x_i=1\) 和 \(x_j=n\) 的两点询问,就可以确定是 \(A\) 还是 \(B\)。
#include<bits/stdc++.h>
using namespace std;
int t,n,a[200005],vis[200005],out;
void solve1(){
int p,ans;
if(out==n) p=n-1;
else p=out+1;
cout<<"? "<<out<<' '<<p<<endl;
cin>>ans;
if(ans==0) cout<<"! A"<<endl;
else cout<<"! B "<<endl;
}
void solve2(){
int minn,maxn,ans1,ans2;
for(int i=1;i<=n;i++){
if(a[i]==1) minn=i;
if(a[i]==n) maxn=i;
}
cout<<"? "<<minn<<' '<<maxn<<endl;
cin>>ans1;
cout<<"? "<<maxn<<' '<<minn<<endl;
cin>>ans2;
if(ans1>=n-1 && ans1==ans2) cout<<"! B"<<endl;
else cout<<"! A"<<endl;
}
int main(){
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
vis[a[i]]=1;
}
for(int i=1;i<=n;i++){
if(!vis[i]) out=i;
}
if(out) solve1();
else solve2();
for(int i=1;i<=n;i++){
vis[i]=0;
}
out=0;
}
return 0;
}
2E & 1B
首先注意到,非 \(0\) 的数一定都能选。
然后注意到,\(0\) 至多只能选一个。
最后注意到,选择靠前的 \(0\),结果一定不劣于选择靠后的 \(0\)。
然后做完了。我们只需要判断选择第一个 \(0\) 和其他非 \(0\) 数,形成的子序列的合法性。
扫两边求前缀 \(min\) 和后缀 \(mex\) 即可。
总体复杂度 \(O(n)\)。
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int t,n,flag,a[200005],minn[200005],mexn[200005],cnt[200005],ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]) ans++;
}
if(ans==n){
cout<<ans<<'\n';
ans=0;
continue;
}
flag=false;
for(int i=1;i<=n;i++){
if(!a[i] && !flag) flag=true;
else if(!a[i]) a[i]=inf;
}
minn[0]=inf;
for(int i=1;i<=n;i++){
minn[i]=min(minn[i-1],a[i]);
}
for(int i=n;i>=1;i--){
mexn[i]=mexn[i+1];
if(a[i]<=n) cnt[a[i]]=1;
while(cnt[mexn[i]]) mexn[i]++;
}
flag=false;
for(int i=1;i<n;i++){
if(minn[i]<mexn[i+1]) flag=true;
}
if(!flag) ans++;
cout<<ans<<'\n';
ans=0;
for(int i=0;i<=n;i++){
minn[i]=mexn[i]=cnt[i]=0;
}
}
return 0;
}
2F & 1C
题面有点神秘。
实际上就是让你对每次操作后,都有 \((P,Q,R)\) 中两者相等。
不妨设 \(pre_i =\oplus_{j=1}^{i} a_j\)。
又因为第 \(i\) 次操作后,\(P \oplus Q \oplus R=pre_i\),所以 \((P,Q,R)\) 一定形如 \((pre_i,x,x)\) 或 \((x,pre_i,x)\) 或 \((x,x,pre_i)\)。
考虑 DP,设 \(f_{i,x}\) 表示前 \(i\) 次操作后,\((P,Q,R)\) 形如 \((pre_i,x,x)\) 或 \((x,pre_i,x)\) 或 \((x,x,pre_i)\) 的方案数。
考虑因为 \(x \oplus a_i \neq x\),所以当 \(x \neq pre_{i-1}\) 时 \(f_{i,x}=f_{i-1,x}\)。
所以只需要考虑 \(x=pre_{i-1}\) 的情况。
考虑怎样的三元组可以通过一次操作到 \(f_{i,pre_i}\),发现只有 \(f_{i-1,pre_{i-1}}\) 和 \(f_{i-1,pre_i}\),也就是说:
答案就是 \(\sum_{x} f_{n,x}\)。
然后把第一维压了,用 umap 维护合法状态,总体复杂度 \(O(n)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int t,n,a[200005],pre[200005],ans;
map<int,int> f;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=pre[i-1]^a[i];
}
f[0]=1;
for(int i=1;i<=n;i++){
f[pre[i-1]]=(3*f[pre[i-1]]%mod+2*f[pre[i]]%mod)%mod;
}
for(map<int,int>::iterator it=f.begin();it!=f.end();it++){
ans=(ans+it->second)%mod;
}
cout<<ans<<'\n';
ans=0;
f.clear();
}
return 0;
}
1D1
趣味组合题。
考虑从小到大考虑每个人,对于当前第 \(i\) 个人,他所能选择的位置是大小为 \(c\) 的位置集合的一个子集,因为编号小的人不会对编号大的人产生任何影响。
因此,考虑 DP。设 \(f_{i,j}\) 表示考虑前 \(i\) 个人,确定了 \(j\) 个位置的方案数。
不难得到转移:
初始 \(f_{1,c}=1\)。
需要滚动数组,总体复杂度 \(O(nmc)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int t,n,m,c,x,f[2][10001],C[101][101];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
C[0][0]=1;
for(int i=1;i<=100;i++){
C[i][0]=1;
for(int j=1;j<=i;j++){
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
cin>>t;
while(t--){
cin>>n>>c>>m;
for(int i=1;i<=m;i++){
cin>>x;
}
for(int i=0;i<=m;i++){
f[0][i]=f[1][i]=0;
}
f[1][c]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<=m;j++){
f[i&1][j]=0;
}
for(int j=0;j<=m;j++){
for(int k=0;k<=min(c,j);k++){
f[i&1][j]=(f[i&1][j]+C[c][k]*f[(i-1)&1][j-k]%mod)%mod;
}
}
}
cout<<f[n&1][m]<<'\n';
}
return 0;
}

浙公网安备 33010602011771号