【超好懂的比赛题解】第十八届同济大学程序设计竞赛暨高校网络友谊赛

57 篇文章 0 订阅
12 篇文章 1 订阅

title : 第十八届同济大学程序设计竞赛暨高校网络友谊赛
date : 2022-5-30
tags : ACM,题解,练习记录
author : Linno


第十八届同济大学程序设计竞赛暨高校网络友谊赛

题目链接:https://ac.nowcoder.com/acm/contest/16832

A-自适应树游走协议

签到题,虽然说签了很久。题意是满二叉树中每个结点有状态01。如果有结点的两个叶子儿子都是1则会发生冲突,并且这个信息会向上传递。已知检查一个结点有无冲突和解决冲突叶子都需要一个时间单位,问需要花费多少时间解决冲突。

子树中有两个以上1的叶子就需要继续遍历,记录子树中1的个数,把树建出来向上求和即可。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e6+7;
const int mod=1e9+7;

#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
int n,k,idx=0,tr[N<<2],lf[N<<2];
inline void update(int p,int l,int r,int pos){
	if(l==r){
		tr[p]=1;
		return;
	}
	if(pos<=mid) update(ls,l,mid,pos);
	else update(rs,mid+1,r,pos);
	tr[p]=tr[ls]+tr[rs];
}

inline void query(int p){
	++idx;
	if(tr[p]<=1) return;
	query(ls);
	query(rs);
}

void Solve(){
	cin>>n>>k;
	for(int i=1,p;i<=k;++i){
		cin>>p;
		update(1,1,n,p);
	}
	query(1);
	cout<<idx<<"\n";
}

signed main(){
	int T=1;
	while(T--){
		Solve();
	}
	return 0;
}

B-简单的数学题

数学不太会,题解区有。以后自己能写出来再补上。

https://blog.nowcoder.net/n/90a3fa421572421e98a2b1309ff972ad

C-困难的数学题

给定正整数n,将其分解为若干个不小于k的正整数之和,有多少种方案?(顺序不同的划分也视为不同的方案)

打个表就可以找到规律了,硬推比较难得出转移方程。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e6+7;
const int mod=1e9+7;

int S[N],n,k,ans;

void Solve(){
	cin>>n>>k;
	for(int i=0;i<k;++i) S[i]=0;
	S[k]=1;
	for(int i=k+1;i<=n;++i){
		S[i]=(S[i-1]+S[i-k])%mod;
	}
	cout<<S[n]<<"\n";
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

D-平衡的字符串

给定01和?组成的串,?可以当作0或者1,问能否使得所有长度为k的子串中0和1的个数相等。

首先k是奇数肯定不行。发现只要第i为是0/1,那么第i+k位也一定是一样的数,因此我们可以全部映射到前面k位,并且判断能否把0和1都填成k/2个即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e6+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,k,f[N];
string str;

void Solve(){
	cin>>n>>k;
	cin>>str;
	int flag=0,idx=0,xx=0;
	if(k&1||(n<k)){
		cout<<"No\n";
		return;
	}else{
		for(int i=0;i<n;++i){
			if(str[i]=='1'){
				if(f[i%k]=='0') flag=1;
				else f[i%k]='1';
			}else if(str[i]=='0'){
				if(f[i%k]=='1') flag=1;
				else f[i%k]='0';
			}
		}
		for(int i=0;i<k;++i){
			if(f[i]=='0') ++idx;
			else if(f[i]=='1') --idx;
			else ++xx;
		}
		if(abs(idx)>xx) flag=1;
	}
	if(flag) cout<<"No\n";
	else cout<<"Yes\n";
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

E-不平衡的字符串

也不太会,以后再补。

https://blog.nowcoder.net/n/8a09e1a2772c48959d7290e1dca67df7

F-值钱的项链

在换中的每个位置填上红色或者蓝色的珠子,规定不能连续两个红色,求最大价值。

起始位红色做一遍,蓝色做一遍dp,然后取最大值即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

int n,m,ans,vis[N],mx[N][2],dp[N][2];
//int val[N][N],col[N][N];
vector<vector<int>>val,col;

void Solve(){
	cin>>n>>m;
	val.resize(n+1);
	col.resize(n+1);
	for(int i=1;i<=n;++i){
		val[i].emplace_back(0);
		for(int j=1,x;j<=m;++j){
			cin>>x;
			val[i].emplace_back(x);
		}
	}
	for(int i=1;i<=n;++i) mx[i][0]=mx[i][1]=-1;
	for(int i=1;i<=n;++i){
		col[i].emplace_back(0);
		for(int j=1,x;j<=m;++j){
			cin>>x;
			col[i].emplace_back(x);
			if(col[i][j]&1){
				mx[i][1]=max(mx[i][1],val[i][j]);//该位置价值最大的红颜色			
			}else{
				mx[i][0]=max(mx[i][0],val[i][j]);//该位置价值最大的蓝颜色	
			}
		}
	}
	dp[1][0]=mx[1][0],dp[1][1]=-1;  //第一个是蓝的情况 
	for(int i=2;i<=n;++i){
		if(dp[i-1][0]==-1) dp[i][1]=-1;
		else dp[i][1]=(mx[i][1]==-1)?-1:(dp[i-1][0]+mx[i][1]);
		if(dp[i-1][1]!=-1&&dp[i-1][0]!=-1){
			dp[i][0]=(mx[i][0]==-1)?-1:(max(dp[i-1][1],dp[i-1][0])+mx[i][0]);
		}else if(dp[i-1][1]!=-1){
			dp[i][0]=(mx[i][0]==-1)?-1:(dp[i-1][1]+mx[i][0]);
		}else if(dp[i-1][0]!=-1){
			dp[i][0]=(mx[i][0]==-1)?-1:(dp[i-1][0]+mx[i][0]);
		}else dp[i][0]=-1;
	}
	ans=max(dp[n][0],dp[n][1]);
	dp[1][1]=mx[1][1],dp[1][0]=-1; //第一个是红,最后一个只能是蓝 
	for(int i=2;i<=n;++i){
		if(dp[i-1][0]==-1) dp[i][1]=-1;
		else dp[i][1]=(mx[i][1]==-1)?-1:(dp[i-1][0]+mx[i][1]);
		if(dp[i-1][1]!=-1&&dp[i-1][0]!=-1){
			dp[i][0]=(mx[i][0]==-1)?-1:(max(dp[i-1][1],dp[i-1][0])+mx[i][0]);
		}else if(dp[i-1][1]!=-1){
			dp[i][0]=(mx[i][0]==-1)?-1:(dp[i-1][1]+mx[i][0]);
		}else if(dp[i-1][0]!=-1){
			dp[i][0]=(mx[i][0]==-1)?-1:(dp[i-1][0]+mx[i][0]);
		}else dp[i][0]=-1;
	}
	if(n==1) cout<<max(mx[1][0],mx[1][1])<<"\n";
	else cout<<max(ans,dp[n][0])<<"\n"; 
	for(int i=1;i<=n;++i) col[i].clear(),val[i].clear();
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

G-自行车调度

给一张图,每个图上都有点权,每条边有单位花费,可以支付代价转移点的若干点权。求所有点点权相同的最小代价。

最小费用流的裸题。知道怎么建模就行了:比平均值大的点权和源点相连,否则和汇点相连,流量就是这个差值,然后点之间不限流量,规定单位费用就可以跑了。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=505,M=1e5+7;
typedef long long ll;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(ll x){if(x>9) write(x/10);putchar(x%10+'0');}

struct E{int v,w,f,nxt;}e[M];
int head[N],cnt=1;
inline void addedge(int u,int v,int w,int f){e[++cnt]=(E){v,w,f,head[u]};head[u]=cnt;}

int n,m,a[N];
int inq[N],pre[N];
ll mcost,mflow,sum,need,flow[N],dis[N];

inline bool spfa(int s,int t){
	for(int i=0;i<=n+1;++i) dis[i]=inf,inq[i]=0,flow[i]=0;
	queue<int>q;
	q.push(s);
	inq[s]=1;dis[s]=0;flow[s]=inf;
	while(q.size()){
		int fro=q.front();
		q.pop();
		inq[fro]=0;
		for(int i=head[fro];i;i=e[i].nxt){
			int to=e[i].v,w=e[i].w,f=e[i].f;
			if(w&&dis[to]>dis[fro]+f){
				dis[to]=dis[fro]+f;
				flow[to]=min(flow[fro],(ll)w);
				pre[to]=i;
				if(!inq[to]){
					q.push(to);
					inq[to]=1;
				}
			}
		}
	}
	return dis[t]!=inf;
} 

inline void update(int s,int t){
	int x=t;
	while(x!=s){
		int i=pre[x];
		e[i].w-=flow[t];
		e[i^1].w+=flow[t];
		x=e[i^1].v;
	}
	mflow+=flow[t];
	mcost+=(ll)flow[t]*(ll)dis[t];
} 

inline void EK(int s,int t){
	while(spfa(s,t)){
		update(s,t);
	}
}

void solve(){
	sum=0;cnt=1;need=0;mflow=0;mcost=0;
	memset(head,0,sizeof(head));
	memset(flow,0,sizeof(flow));
	memset(pre,0,sizeof(pre));
	n=read();m=read();
	for(int i=1;i<=n;++i) a[i]=read(),sum+=a[i];
	for(int i=1,u,v,w;i<=m;++i){
		u=read();v=read();w=read();
		addedge(u,v,inf,w); //单位代价
		addedge(v,u,0,-w);
		addedge(v,u,inf,w);
		addedge(u,v,0,-w);
	}
	if(sum%n!=0){
		puts("-1");
	}else{
		sum/=n;
		for(int i=1;i<=n;++i){
			if(a[i]>sum) addedge(0,i,a[i]-sum,0),addedge(i,0,0,0),need+=a[i]-sum;
			else addedge(i,n+1,sum-a[i],0),addedge(n+1,i,0,0);
		}
		EK(0,n+1);
		if(mflow==need) write(mcost),putchar('\n');
		else puts("-1");
	}
} 

signed main(){
	int t=read();
	while(t--){
		solve();
	}
	return 0;
}

H- 三阳开泰

给定A,B,C,X,问有多少a,b,满足
a ≤ A , b ≤ B , c ≤ C a   xor ⁡   b ≤ X , b   xor ⁡   c ≤ X , c   xor ⁡   a ≤ X a≤A,b≤B,c≤C\\ a\,\operatorname{xor}\,b \le X,b\,\operatorname{xor}\,c\le X,c\,\operatorname{xor}\,a\le X aA,bB,cCaxorbX,bxorcX,cxoraX
数据范围得出是数位DP题,对我来说思维量有点大。跑记忆化搜索,选择当前数位a,b,c填的数,然后往下一位转移,需要记录当前为a,b,c能填的最大数以及三个异或值当前位能填的最大数。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=1e6+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int a,b,c,x,cnta,cntb,cntc,cntx;
int aa[N],bb[N],cc[N],xx[N];
int dp[100][2][2][2][2][2][2];
/*
对于二进制的每一位i(从高到低),考虑a,b,c能填0~num[i]之中的哪些数字
填上这些数字之后判断a^b,b^c,c^a是否大于x,合法则计算贡献。 
*/
inline int dfs(int pos,int la,int lb,int lc,int lab,int lac,int lbc){
	if(pos==-1) return 1;
	int &ans=dp[pos][la][lb][lc][lab][lac][lbc];
	if(ans!=-1) return ans;
	ans=0;
	int upa=la?aa[pos]:1;
	int upb=lb?bb[pos]:1;
	int upc=lc?cc[pos]:1;
	int upx=xx[pos];
	for(int i=0;i<=upa;++i){
		for(int j=0;j<=upb;++j){
			for(int k=0;k<=upc;++k){
				if(lab&&((i^j)>xx[pos])) continue;
				if(lac&&((i^k)>xx[pos])) continue;
				if(lbc&&((j^k)>xx[pos])) continue;
				ans=(ans+dfs(pos-1,la&&(i==upa),lb&&(j==upb),lc&&(k==upc),lab&&(i^j==xx[pos]),lac&&(i^k==xx[pos]),lbc&&(j^k==xx[pos]))%mod)%mod;
			}
		}
	}
	return ans;
}

void Solve(){
	cin>>a>>b>>c>>x;
	memset(aa,0,sizeof aa);
	memset(bb,0,sizeof bb);
	memset(cc,0,sizeof cc);
	memset(xx,0,sizeof xx);
	memset(dp,-1,sizeof dp);
	cnta=cntb=cntc=cntx=0;
	while(a) aa[cnta++]=a&1,a>>=1;
	while(b) bb[cntb++]=b&1,b>>=1;
	while(c) cc[cntc++]=c&1,c>>=1;
	while(x) xx[cntx++]=x&1,x>>=1;
	cout<<dfs(63,1,1,1,1,1,1)<<"\n";
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}

I-图中修边

一次操作可以使a和b之间新增一条边,a与c,b与c之间的两条边消失。

问,给定的简单无向图是否可以在有限次操作之后,令图中所有点的度数小于等于1。

判一下每个连通块是否是完全图或者欧拉图即可,可以并查集做。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+7;

int fa[N],u[N],v[N],sz[N],sum[N],val[N];
int n,m,deg[N],dfn[N],flag,res;
inline int find(int x){return (fa[x]==x)?x:(fa[x]=find(fa[x]));}

void Solve(){
	cin>>n>>m;
	for(int i=1;i<=n;++i) deg[i]=dfn[i]=sz[i]=val[i]=sum[i]=0;
	flag=0;
	for(int i=1;i<=n;++i){fa[i]=i;sz[i]=1;}
	for(int i=1;i<=m;++i){
		cin>>u[i]>>v[i];
//		addedge(u,v); addedge(v,u);
		++deg[u[i]];++deg[v[i]];
	}
	for(int i=1;i<=n;++i){
		if(deg[i]>1&&deg[i]%2==0){
			val[i]=1;
		}
		sum[i]=deg[i];
	}
	for(int i=1,fx,fy;i<=m;++i){
		fx=find(u[i]),fy=find(v[i]);
		if(fx!=fy){
			fa[fx]=fy;
			sz[fy]+=sz[fx];
			sum[fy]+=sum[fx];
			val[fy]+=val[fx];
		}
	}
	int flag=1;
	for(int i=1;i<=n;++i){
		if(fa[i]==i){
			if(val[i]==sz[i]) flag=0;
			if(sum[i]==sz[i]*(sz[i]-1)&&sz[i]>2) flag=0;
		}
	}
	if(flag) cout<<"YES\n";
	else cout<<"NO\n";
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

J-下围棋

博弈,给定一棵树,两人轮流裁剪任意非根结点及其所在的子树,不能操作的人输。

树上删边的裸题,每个点的SG值由其儿子(异或)决定,然后考虑叶子结点SG值是1。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,sg[N];
vector<int>G[N];
inline void dfs(int x,int f){
	sg[x]=0;
	for(auto to:G[x]){
		if(to==f) continue;
		dfs(to,x);
		sg[x]^=(sg[to]+1);
	}
}

void Solve(){
	cin>>n;
	for(int i=1,u,v;i<n;++i){
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0);
	if(sg[1]) cout<<"NO\n";
	else cout<<"YES\n";
	for(int i=1;i<=n;++i) G[i].clear();
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值