CF Round 1004 题解合集

here.

here.

感觉是 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}\),也就是说:

\[f_{i,x}=3f_{i-1,pre_{i-1}}+2f_{i-1,pre_{i}},x=pre_{i-1} \]

\[f_{i,x}=f_{i-1,x},x \neq pre_{i-1} \]

答案就是 \(\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_{i,j}=\sum_{k=0}^{c} \binom{c}{k} f_{i-1,j-k} \]

初始 \(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;
}
posted @ 2025-02-13 10:17  _Kenma  阅读(38)  评论(0)    收藏  举报