牛客周赛 Round 68 个人题解 (A~F)

牛客周赛 Round 68 个人题解 (A~F)

牛客周赛 Round 68

A. 三途川的摆渡人(二)

解题思路

  • 直接统计0,1个数
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve(){
	int n;cin>>n;
	string s;cin>>s;
	s=" "+s;
	int ans=0;
	for(int i=1;i<=n;i++){
		if(s[i]=='0') ans++;
	}
	cout<<ans<<endl;

}
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T=1;
	while(T--) solve();
	return 0;
}

B. 魔法之森的蘑菇(二)

解题思路

  • 太久没做题了,二维前缀和公式不会推,难绷
  • n4枚举左上角和右下角端点,然后二位前缀和预处理即可,二维前缀和公式为sum[x][y]-sum[x][j-1]-sum[i-1][y]+sum[i-1][j-1]
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
char g[40][40];
int sum[40][40];
void solve(){
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>g[i][j];
			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
			if(g[i][j]=='.') sum[i][j]++;
		}
	}
	int ans=0;
	int l1,r1,l2,r2;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int x=i;x<=n;x++){
				for(int y=j;y<=m;y++){
					int t=sum[x][y]-sum[x][j-1]-sum[i-1][y]+sum[i-1][j-1];
					if(t==(x-i+1)*(y-j+1) && t>ans){
						ans=max(ans,t);
						l1=i,r1=j,l2=x,r2=y;
					}
				}
			}
		}
	}
	cout<<l1<<" "<<r1<<" "<<l2<<" "<<r2<<endl;
}
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T=1;
	while(T--) solve();
	return 0;
}

C. 迷途之家的大贤者(二)

题目分析

  • 先计算两个数组内部自己相同的数,这些是至少要操作的部分,即为操作1,然后统计两边共有的数字的个数,这部分的数字删除有两种途径,一种是跟着操作1删除,一种是两边内部没有相同的数之后,同时相同,计算这几部分的值相加即可
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N],b[N];
void solve(){
	int n;cin>>n;
	map<int,int> mpa;
	map<int,int> mpb;
	int cnt1=0,cnt2=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		mpa[a[i]]++;
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
		mpb[b[i]]++;
	}
	for(auto [x,y]:mpa) cnt1+=y-1;
	for(auto [x,y]:mpb) cnt2+=y-1;
	int ans=0;
	if(cnt1>cnt2){
		int tot=0;
		int d=cnt1-cnt2;
		for(auto [x,y]:mpa){
			if(mpb[x]) tot++;
		}
		if(tot<=cnt1-cnt2) ans=cnt1;
		else ans=cnt1+(tot-d+1)/2;
	}
	else{
		int tot=0;
		int d=cnt2-cnt1;
		for(auto [x,y]:mpb){
			if(mpa[x]) tot++;
		}
		if(tot<=d) ans=cnt2;
		else ans=cnt2+(tot-d+1)/2;
	}
	cout<<ans<<endl;

}
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T=1;  
	while(T--) solve();
	return 0;
}

D. 红魔馆的馆主(二)

题目分析

  • 495=3*3*5*11,所以只需要同时含有这四个质因子,则满足乘积为495的倍数,我们状压处理四位表示每一位是否含有这四个质因子,那么两数相乘若i|j==15,则满足题意,还有一种情况是1101 | 0001,这也是满足情况的
  • 即当i|j==13 且i,j第一位均为1时满足题意
  • 加1操作怎么处理呢?考虑记录前缀和与后缀和,先计算不进行加1操作的答案,枚举a[i],只需枚举j使得calc(i)与j满足上述两种情况之1,则ans+=pre[i-1][j]
  • 那么枚举+1的数,然后通过前后缀快速计算,新增加的贡献
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=4e5+10;
int n;
int a[N],v[N],pre[N][20],suf[N][20];
int calc(int x){
	int res=0;
	if(x%3==0) res|=1;
	if(x%9==0) res|=2;
	if(x%5==0) res|=4;
	if(x%11==0) res|=8;
	return res;
} 
bool check(int j,int x){
	return (j|x)==15 || ( (j|x)==13 && ((j&1) && (x&1)) );
}
void init(){
	for(int i=1;i<=n;i++) v[i]=calc(a[i]);
	for(int i=1;i<=n;i++){
		for(int j=0;j<=15;j++){
			pre[i][j]=pre[i-1][j];
		}
		pre[i][v[i]]++;
	}
	for(int i=n;i>=1;i--){
		for(int j=0;j<=15;j++){
			suf[i][j]=suf[i+1][j];
		}
		suf[i][v[i]]++;
	}
}
void solve(){
	cin>>n;
	int ans=0;
	for(int i=1;i<=n;i++) cin>>a[i];
	init();
	for(int i=1;i<=n;i++){
		for(int j=0;j<=15;j++){
			if(check(j,v[i])) ans+=pre[i-1][j];
		}
	}
	int maxn=0;
	for(int i=1;i<=n;i++){
		int sum=0;
		int x=a[i];
		x++;
		int tmp=calc(x);
		for(int j=0;j<=15;j++){
			if(check(j,v[i])){
				sum=sum-pre[i-1][j]-suf[i+1][j];
			}
			if(check(j,tmp)){
				sum=sum+pre[i-1][j]+suf[i+1][j];
			}
		}
		maxn=max(maxn,sum);
	}
	cout<<ans+maxn<<endl;
}
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T=1;
	while(T--) solve();
	return 0;
}

E. 博丽神社的巫女(二)

解题思路

  • 分组背包,第一次做
  • f[i][j]表示前i个数总和为j,状态转移则为f[i][j]=f[i-1][j],枚举j=a[i],每次除以2,枚举总和k,k>=j,则f[i][k] |= f[i-1][k-j]
  • 最后dp完再倒序找方案即可,详细见代码
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
const int MAXN=1e5;
int a[110],f[110][N],ans[110];
void solve(){
	int n;cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=MAXN;j++) f[i][j]=f[i-1][j];
		for(int j=a[i];j>=1;j/=2){
			for(int k=MAXN;k>=j;k--){
				f[i][k]|=f[i-1][k-j];
			}
		}
	}
	if(!f[n][MAXN]){
		cout<<-1<<endl;
		return;
	}
	int res=MAXN;
	for(int i=n;i>=1;i--){
		if(f[i][res]==f[i-1][res]){
			ans[i]=100;
			continue;
		}
		for(int j=a[i],cnt=0;j>=1;j/=2,cnt++){
			if(res-j<0) continue;
			if(f[i][res]==f[i-1][res-j]){
				res-=j;
				ans[i]=cnt;
				break;
			}
		}
	}
	for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
}
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T=1;
	while(T--) solve();
	return 0;
}

F. 雾之湖的冰精(三)

解题思路

  • 树上dp,我们记f[u][i]表示结点u为路径的一个端点,且路径总和不超过i的方案总数
  • alt
  • 考虑图片上的两种情况,对于情况1,可以直接从v的状态转移到u的状态来,对于情况2,当前点会由两个儿子的值得到,这里运用乘法原理,不好描述,详细看代码吧
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int n,ans=0;;
int a[N],f[N][15];
vector<int> g[N];
void dfs(int u,int fa){
	vector<int> s(10); //当前层下的路径数
	int now=a[u];
	for(auto v:g[u]){
		if(v==fa) continue;
		dfs(v,u);
		//情况1的直接转移,即u为一条路径的终点状态
		for(int i=now;i<=9;i++){
			f[u][i]+=f[v][i-now]; 
		}
		//情况2,此时点u作为一条路径上的点(非端点)
		for(int i=0;i<=9;i++){
			for(int j=0;j<=9-i-now;j++){
				ans+=f[v][i]*s[j];  
			}
		}
		//记录当前层下的点
		for(int i=0;i<=9;i++) s[i]+=f[v][i];
	}
	//点u作为终点
	for(int i=0;i<=9;i++) ans+=f[u][i];
	f[u][now]++;
}
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
 	for(int i=1;i<=n-1;i++){
 		int u,v;cin>>u>>v;
 		g[u].push_back(v);
 		g[v].push_back(u);
 	}
 	dfs(1,0);
 	cout<<ans<<endl;
}
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T=1;
	while(T--) solve();
	return 0;
}

posted @ 2024-11-21 17:02  Persona_owl  阅读(26)  评论(0编辑  收藏  举报