ABC 328 题解

发布时间 2023-11-12 11:12:42作者: MC铁锭233

A

直接模拟即可。

cin>>n>>m;
for(int i=1;i<=n;++i){
	int x; cin>>x;
	if(x<=m)sum+=x;
}
cout<<sum;

B

直接模拟即可。

int n,ans;
bool chk(int x,int y){
	int dig=x%10; x/=10;
	while(x){
		if(x%10!=dig)return 0;
		x/=10;
	}
	while(y){
		if(y%10!=dig)return 0;
		y/=10;
	} return 1;
}
void Solve(){
	cin>>n;
	for(int i=1;i<=n;++i){
		int x; cin>>x;
		for(int j=1;j<=x;++j){
			if(chk(i,j))++ans;
		}
	}
	cout<<ans;
}

C

由于没有修改操作,预处理每个会出现重复两次的位置,并且将他们加入数组(或者 \(\texttt{vector}\))中,二分查找即可。

int n,m;
string s;
vector<int>vec;
void Solve(){
	cin>>n>>m;
	cin>>s;
	for(int i=0;i<n-1;++i){
		if(s[i]==s[i+1])vec.push_back(i+1);
	}vec.push_back(1145141919);
	while(m--){
		int l,r; cin>>l>>r;
		int p1=lower_bound(vec.begin(),vec.end(),l)-vec.begin();
		int p2=lower_bound(vec.begin(),vec.end(),r)-vec.begin()-1;
		cout<<p2-p1+1<<'\n';
	}
}

D

注意到删除操作会使得字符串拼接起来,因此考虑使用栈来模拟这个删除过程。

只要栈顶的三个元素是 \(\texttt{C,B,A}\) 就说明字符串中出现了 \(\texttt{ABC}\),栈顶弹出三个元素即可。

int n,m;
string s;
vector<char>vec;
void Solve(){
	cin>>s; int x=0;
	for(char ch:s){
		vec.push_back(ch); ++x;
		if(x>=3){ //栈顶要有三个元素才能判断
			if(vec[x-3]=='A' && vec[x-2]=='B' && vec[x-1]=='C'){
				vec.pop_back(); vec.pop_back(); vec.pop_back(); x-=3;
			}
		}
	}
	for(char ch:vec)cout<<ch;
}

E

发现一个很鬼畜的数据范围:\(1 \leq n \leq 8\)

因此考虑直接顺序搜索每一条可行的边,只要边满足生成树的条件就尝试加入这条边,当选择的边数量是 \(n-1\) 时就更新最终答案,注意取模以及开 \(\texttt{long long}\)

#define N 15
#define int long long
int n,m,k,sum,ans=1e18,u[N],v[N],w[N],f[N],p=1;
int find(int x){
	return x==f[x]?x:f[x]=find(f[x]);
}
vector<int>e[N];
vector<int>vec;
bool chk(int x){ //因为 n<=8 所以每次判断时候直接重新开并查集然后查询即可
	for(int i=1;i<=n;++i)f[i]=i;
	for(int i:vec){
		int fx=find(u[i]),fy=find(v[i]);
		f[fx]=fy;
	} return find(u[x])!=find(v[x]);
}
void dfs(){
	if(vec.size()==n-1){ //vector 存选中的边,数量是 n-1 就更新答案
		ans=min(ans,sum%k);
		return;
	} int t=p; for(int i=p;i<=m;++i){ //顺序搜索
		if(chk(i)){ //这条边是否可行
			vec.push_back(i);
			sum+=w[i]; p=i;
			dfs();
			vec.pop_back(); p=t;
			sum-=w[i];
		}
	}
}
void Solve(){
	cin>>n>>m>>k;
	for(int i=1;i<=m;++i)cin>>u[i]>>v[i]>>w[i];
	dfs(); cout<<ans;
}

F

先读懂题意。发现题目是想要尝试加入编号为 \(i\) 的约束条件 \(X_{a_i}-X_{b_i}=d_i\),因此直接使用带权并查集维护这一过程即可。在有基础的情况下 F 甚至比 E 简单。如果没有写过带权并查集可以写一下洛谷的银河英雄传说一题。

int n,m,q,f[N],g[N];
int find(int x){
	if(f[x]==x)return x;
	int t=f[x]; f[x]=find(f[x]);
	g[x]=g[x]+g[t]; return f[x]; //路径压缩下更新权值
}
void Solve(){
	cin>>n>>q;
	for(int i=1;i<=n;++i)f[i]=i;
	for(int i=1;i<=q;++i){
		int u,v,w; cin>>u>>v>>w;
		int fx=find(u),fy=find(v);
		if(fx==fy && g[u]!=g[v]+w)continue; //当前情况不能满足约束条件
		cout<<i<<" "; if(fx!=fy){
			f[fx]=fy; g[fx]=-g[u]+g[v]+w; //更新权值
		}
	}
}

说句题外话,CF1850H 是类似题,评分 \(1700\)

G

咕咕咕。自己正在补。