2023河南萌新联赛第(一)场:河南农业大学

文章包含了多道编程竞赛题目,涉及数学推理、位运算的线段树、硬币游戏的胜负分析、最短路径问题、区间和的计算、二分查找法在中位数问题中的应用、树上查分问题以及合唱比赛的平均分计算等。每道题目都有详细的解题思路和代码实现。
摘要由CSDN通过智能技术生成

A. 你也喜欢数学吗 

 题解:打表找规律找的

具体咋推得可以看【题解】2023河南萌新联赛第(一)场:河南农业大学_ICPC/CCPC/NOIP/NOI刷题训练题单_牛客竞赛OJ (nowcoder.com)

 

#include<bits/stdc++.h>
#define endl '\n'
#define int long long 
using namespace std;
const int N=2e5+3;
const int mod=1000000007;
void solve() {
	int n;
	cin>>n;
	int x=(__int128)(n)*((n+1))*((n+2))/6%mod;
	cout<<x;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
//	int t;	cin>>t;	while(t--)
	solve();
	return 0;
}


B  Middle 

 题解:将数组每个数拆成2进制数字,线段树存每个数位进行位运算的值

每个数位的答案不是0开头进行运算就是1开头进行运算,具体可见代码注释

 

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int nums[N];
char s[N];
map<char,int>d;
pair<int,int>transformations[3][2];
pair<int,int> tree[4*N][20];
pair<int,int>OP(pair<int,int>a,pair<int,int>b) {
	return make_pair(a.first ? b.second : b.first, a.second ? b.second : b.first);
}
void build(int idx,int l,int r,int bit) {
	if(l==r) {
		int v=nums[l];
		int ops=d[s[l]];
		tree[idx][bit]=transformations[ops][v>>bit&1];//这里就等于x当前bit位 op 当前数位 的值 
	} else {
		int mid=l+(r-l)/2;
		build(idx*2,l,mid,bit);
		build(idx*2+1,mid+1,r,bit);
		tree[idx][bit]=OP(tree[idx*2][bit],tree[idx*2+1][bit]);//  前一个区间值为x当前bit位 op l .....mid的值
		//后一个为 0或者1 op mid+1 .....r的值
		//所以 l....r当前bit的值为取决于 前一个区间xop...l...mid的值
		 

	}
	return ;
}
void update(int idx,int l,int r,int pos,int val,int bit) {
	if(l==r) {
		int ops=d[s[l]];
		tree[idx][bit]=transformations[ops][val>>bit&1];
	} else {
		int mid=l+(r-l)/2;
		if(pos<=mid) {
			update(idx*2,l,mid,pos,val,bit);
		} else {
			update(idx*2+1,mid+1,r,pos,val,bit);
		}
		tree[idx][bit]=OP(tree[idx*2][bit],tree[idx*2+1][bit]);
	}
	return ; 
}
pair<int,int>query(int idx,int l,int r,int ql,int qr,int bit) {
	if(ql<=l&&r<=qr) {
		return tree[idx][bit];
	}
	int mid=l+(r-l)/2;
	if(qr<=mid) {
		return query(idx*2,l,mid,ql,qr,bit);
	}
	if(ql>mid) {
		return query(idx*2+1,mid+1,r,ql,qr,bit);
	}
	return OP(query(idx*2,l,mid,ql,qr,bit),query(idx*2+1,mid+1,r,ql,qr,bit));
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	d['|']=0;
	d['^']=1;
	d['&']=2;
	transformations[0][0] = make_pair(0, 1);//如果当前数位为0,X数位为O 则第一个值为0 第二值为1 
	transformations[0][1] = make_pair(1, 1);//如果当前数位为1,X数位为O 则第一个值为1 第二值为1  因为1|任何数字都等于1,剩下同理。 
	transformations[1][0] = make_pair(0, 1);
	transformations[1][1] = make_pair(1, 0);
	transformations[2][0] = make_pair(0, 0);
	transformations[2][1] = make_pair(0, 1);
	int n;
	cin>>n;

	for(int i=0; i<n; ++i) {
		cin>>s[i];
	}
	for(int i=0; i<n; ++i) {
		cin>>nums[i];
	}
	for(int bit=0; bit<20; ++bit) {
		build(1,0,n-1,bit);
	}
	int q;
	cin>>q;
	for(int i=0; i<q; i++) {
		int op;
		cin>>op;
		if(op==1) {
			int pos,val;
			cin>>pos>>val;
			pos--;
			for(int bit=0; bit<20; ++bit) {
				update(1,0,n-1,pos,val,bit);
			}
		} else {
			int x,l,r;
			cin>>x>>l>>r;
			l--;
			r--;
			int res=0;
			for(int bit=0; bit<20; ++bit) {
				pair<int,int>tmp=query(1,0,n-1,l,r,bit);

				if(x>>bit&1) {
					res|=tmp.second<<bit;//是1就是后面的值 
				} else {
					res|=tmp.first<<bit;
				}
			}
			cout<<res<<'\n';
		}
	}
}

 代码借鉴与2023河南萌新联赛第(一)场:河南农业大学 - 知乎 (zhihu.com)

 

C. 硬币游戏 

 题解:其实你模拟一下过程就可以发现,如果当前全是1或者全是0的话,如果长度大于k就是后手赢,否则就是先手赢。然后我们将可以一下更改区间的值记作一段,如果是1或则是0

更改区间只有一段 则先手赢。然后思考发现,当后手赢的话只有以下可能 

s=="0101"||s=="1010"||s=="1100"||s=="0110"||s=="0011"||s=="1001"

其他都是平局

# include <bits/stdc++.h>
using namespace std;
int n,k;
string s;
int main() {
	cin>>n>>k;
	cin>>s;
	int ans,ans1;
	int cnt,cnt1;
    cnt=cnt1=0;
	ans=ans1=0;
	int flag;
	if(s[0]=='0') {
		flag=0;
		ans++;
	} else {
		flag=1;
		ans1++;
	}
	for(int i=1; i<s.length(); i++) {
		if(s[i]=='0'&&!flag) {
			ans++;
			if(ans>k) {
				ans-=k;
				cnt+=1;
			}
		} else if(s[i]=='0'&&flag) {
			cnt1+=1;
			ans1=0;
			ans++;
			flag=0;
		} else if(s[i]=='1'&&flag) {
			ans1++;
			if(ans1>k) {
				ans1-=k;
				cnt1++;
			}
		} else if(s[i]=='1'&&!flag) {
			flag=1;
			cnt++;
			ans=0;
			ans1++;
		}
	}
	if(ans) {
		cnt+=1;
	}
	if(ans1) {
		cnt1+=1;
	}
	if(cnt==0||cnt1==0) {
        if(n>k)
        {
             cout<<"Bob"<<endl;
        }
        else{
             cout<<"Alice"<<endl;
         }
	} else if(cnt==1||cnt1==1) {
		cout<<"Alice"<<endl;
	} 
    else if(s=="0101"||s=="1010"||s=="1100"||s=="0110"||s=="0011"||s=="1001") {
		cout<<"Bob"<<endl;
		return 0;
	}
	 else {
		cout<<":("<<endl;
	}
}

D. 松鼠回家

题解 :正解是二分加最短路 

但比赛时减枝也过了,有可能数据水了,代码如下; 

# include <bits/stdc++.h>
using namespace std;
# define int long long
const int M=2*1e5+10;
int head[4*M],Next[3*M],edge[4*M],ver[4*M];
int cnt=0;
int n,m,st,ed,h1;
void add(int x,int y ,int z) {
	ver[++cnt]=y;
	edge[cnt]=z;
	Next[cnt]=head[x];
	head[x]=cnt;
}
int a[M];
int book[M];
const int inf=1e18;
int step=inf;
void dfs(int x,int fa,int h,int v) {
	if(v>step)
	{
		  return ;
	}
	if(x==ed) {
		step=min(step,v);
		return ;
	}
	for(int i=head[x]; i; i=Next[i]) {
		int y=ver[i];
		int v1=edge[i];
		if(y!=fa&&h-v1>=0&&!book[y]) {

            book[y]=1;
			dfs(y,x,h-v1,max(v,a[y]));
			book[y]=0;
		}
	}
	return ;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin>>n>>m>>st>>ed>>h1;
	for(int i=1; i<=n; i++) {
		cin>>a[i];
	}
	for(int i=1; i<=m; i++) {
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
		add(y,x,z);
	}
	dfs(st,-1,h1,a[st]);
	if(step==inf) {
		cout<<"-1"<<endl;
	} else {
		cout<<step<<endl;
	}
}

 E. 动物朋友

题解: 题目问你由几种区间和可以等于m 我们从开头开始进行前缀和,如果当前和大于m

以后连续数字不可能等于m于是我们必须去掉前面的数字直到区间数字小与m 这样后面才有可能等于m

# include <bits/stdc++.h>
# include <deque>
using namespace std;
const int N=1e6+10;
char s[N];
int   a[N];
deque<int>q; 
int main() {
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		  cin>>a[i];
	}
	int sum=0;
	int res=0;
	for(int i=1;i<=n;i++)
	{
		  sum+=a[i];
		  q.push_back(a[i]);
		  while(sum>=m&&q.size())
		  {
		  	    if(sum==m)
		  	    {
		  	    	  res++;
				  }
		  	     sum-=q.front();
		  	    q.pop_front();
		  }
	}

	cout<<res<<endl;
}

 

 F. 松鼠排序

题解:如果 当前排列1不在首位,我们必须要先将1放在首位才能进行下面的置换,之后的数字同理 

# include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char s[N];
int   a[N];
map<int,int>d;
int main() {
	int n;
	cin>>n;
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		  cin>>a[i];
		  d[a[i]]=i;
     }
     for(int i=1;i<=n;i++)
     {
     	  if(d[i]!=i)
     	  {
     	  	    sum+=1;
     	  	    d[a[i]]=d[i];
     	  	    swap(a[d[i]],a[i]);
     	  	    d[i]=i;
     	  	    
		   }
	 }
	
	cout<<sum<<endl; 
}

 G. Reverse

题意:思维题 结果最大就是求俩段全是1的长度最大和 

# include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char s[N];
int a[N];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int n;
	cin>>n;
	cin>>(s+1);
	int cnt=0;
	int res=0;
	for(int i=1; i<=n; i++) {
		if(s[i]=='1') {
			cnt++;
		} else if(s[i]=='0') {
			if(cnt) {
				a[res++]=cnt;
				cnt=0;
			}
		}
	}
	if(cnt)
	{
		 a[res++]=cnt;
	}
	sort(a,a+res);
	 reverse(a,a+res);
	 cout<<a[0]+a[1]<<endl;
}

H 迷宫探险 

题解:最短路 就是最短路跑一遍就可以了,细节就是如果当前是弹簧,就跳,时间不加一,不然时间就加一计算。

具体看代码 

# include <bits/stdc++.h>
using namespace std;
# define int long long
const int N=3015;
char  s[N][N];
int book[N][N];
int book1[N][N];
const int inf=1e18;
int nex[4]= {1,-1,0,0};
int ney[4]= {0,0,1,-1};
signed  main() {
	int n,m;
	cin>>n>>m;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			cin>>s[i][j];
			book[i][j]=inf;
		}
	}
	int q1;
	cin>>q1;
	while(q1--) {
		int x,y,z;
		cin>>x>>y>>z;
		book1[x][y]=z;
	}
	set<pair<int,pair<int,int>>>q;
	q.insert({0,{1,1}});
	book[1][1]=0;
	while(!q.empty()) {
		pair<int,pair<int,int>>d= *q.begin();
		q.erase(q.begin());
		if(s[d.second.first][d.second.second]=='*') {
			for(int i=0; i<=3; i++) {
				int x1=d.second.first+nex[i]*book1[d.second.first][d.second.second];
				int y1=d.second.second+ney[i]*book1[d.second.first][d.second.second];
				if(x1>=1&&x1<=n&&y1>=1&&y1<=m&&s[x1][y1]!='#') {
					if(book[x1][y1]>d.first) {
						q.erase({book[x1][y1],{x1,y1}});
						book[x1][y1]=d.first;
						q.insert({book[x1][y1],{x1,y1}});
					}
				}

			}
			continue ;
		}
		for(int i=0; i<=3; i++) {
			int x1=d.second.first+nex[i];
			int y1=d.second.second+ney[i];
			if(x1>=1&&x1<=n&&y1>=1&&y1<=m&&s[x1][y1]!='#') {
				if(book[x1][y1]>d.first+1) {
					q.erase({book[x1][y1],{x1,y1}});
					book[x1][y1]=d.first+1;
					q.insert({book[x1][y1],{x1,y1}});

				}
			}
		}
	}
	if(book[n][m]==inf) {
		cout<<"-1"<<endl;
	} else {
		cout<<book[n][m]<<endl;
	}
}

 

L 中位数

 题解:其实就是建一个可以更改 数值并可以求前k最小值的线段树板子题,

只不过这个线段树是按值域建的,具体看代码。

 

# include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int a[N],b[N];
int tr[N*4];
void pushup(int u) {
	tr[u]=tr[u<<1]+tr[u<<1|1];
}
void update(int u,int l,int r,int v,int y) {
	if(l==r) tr[u]+=y;
	else {
		int mid=l+r>>1;
		if(v<=mid) update(u<<1,l,mid,v,y);
		else update(u<<1|1,mid+1,r,v,y);
		pushup(u);
	}
}
int query(int u,int l,int r,int k) {
	if(l==r) return l;
	int mid=l+r>>1;
	if(k<=tr[u<<1]) return query(u<<1,l,mid,k);
	return query(u<<1|1,mid+1,r,k-tr[u<<1]);
}
int main() {
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++) {
		scanf("%d",&a[i]);
		update(1,1,N,a[i],1);
	}
	for(int i=1; i<=m; i++) {
		int x,y;
		cin>>x>>y;
		update(1,1,N,a[x],-1);
		a[x]=y;
		update(1,1,N,y,1);
		cout<<query(1,1,N,(n+1)/2);
		cout<<endl;
	}
	return 0;
}

 I 松鼠采松果

 题解:树上查分板子题 首先你要会lca 然后p[x]就是当前节点到根节点所有边之和,求x到y路径权值和是p[x]+p[y]-p[lca(x,y)]*2.至于p[x]怎么求的就是利用差分求和得到的,你可以根据下面代码模拟一下树上差分的过程。

# include <bits/stdc++.h>
using namespace std;
# define int long long
const int N=4*5*1e5+10;
int n,m,tot,ans,p[N],f1[N],lin[N],f[N][25],d[N],vis[N];
int head[N],edge[N],ver[N],Next[N];
int cnt=0;
void add(int a,int b,int c) {
	ver[++cnt]=b;
	edge[cnt]=c;
	Next[cnt]=head[a];
	head[a]=cnt;
}
void bfs() {
	queue<int>q;
	q.push(1);
	d[1]=1;
	while(q.size()) {
		int x=q.front();
		q.pop();
		for(int i=head[x]; i; i=Next[i]) {
			int y=ver[i];
			if(d[y]) {
				continue ;
			}
			p[y]+=edge[i];
			p[x]-=edge[i];
			d[y]=d[x]+1;
			f[y][0]=x;
			for(int j=1; j<=23; j++) {
				f[y][j]=f[f[y][j-1]][j-1];
			}
			q.push(y);
		}
	}
}
int lca(int x,int y) {
	if(d[x]>d[y]) {
		swap(x,y);
	}
	for(int i=23; i>=0; i--) {
		if(d[f[y][i]]>=d[x]) {
			y=f[y][i];
		}
	}
	if(x==y) {
		return x;
	}
	for(int i=23; i>=0; i--) {
		if(f[x][i]!=f[y][i]) {
			x=f[x][i],y=f[y][i];
		}
	}
	return f[x][0];
}
void  dfs(int x) {
	vis[x]=1;
	for(int i=head[x]; i; i=Next[i]) {
		int y=ver[i];
		if(vis[y]) {
			continue ;
		};
		p[y]+=p[x];
		dfs(y);
	}
}
void dfs1(int x) {
	vis[x]=1;
	for(int i=head[x]; i; i=Next[i]) {
		int y=ver[i];
		if(vis[y]) {
			continue ;
		};
		dfs1(y);
		p[x]+=p[y];
	}
}
signed main() {

	int q;
	cin>>n>>m>>q;
	for(int i=1; i<n; i++) {
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
		add(y,x,z);
	}
	bfs();
	for(int i=1; i<=m; i++) {
		int x,y,z;
		cin>>x>>y>>z;
		p[x]+=z;
		p[y]+=z;
		p[lca(x,y)]-=2*z;
	}
	dfs1(1);
	memset(vis,0,sizeof(vis));
	dfs(1);
	for(int i=1; i<=q; i++) {
		int x,y;
		cin>>x>>y;
		cout<<p[x]+p[y]-2*p[lca(x,y)]<<endl;
	}



}

 

J 合唱比赛  

题解:最高分就是去掉一个最低分的平均值,最低分就是去掉一个最高分的平均值 

# include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char s[N];
double  a[N];
int main() {
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		  cin>>a[i];
	}
	sort(a+1,a+n+1);
	double r,l;
	r=l=0;
    for(int i=2;i<=n;i++)
    {
    	  r+=a[i];
	}
	r=r/(n-1);
	for(int i=1;i<=n-1;i++)
	{
		l+=a[i];
	}
	l=l/(n-1);
	printf("%.6lf %.6lf",l,r);
	
}

 K 以撒和隐藏房间 

 题解:根据题意对于每个是墙的根据题意判断就行,坑点就是普通房间就只能是3个

# include <bits/stdc++.h>
using namespace std;
const int N=2000;
char s[N][N];
int n,m;
int nex[4]= {1,-1,0,0};
int ney[4]= {0,0,1,-1};
bool check(int x,int y) {
	int flag=0;
	int res=0;
	for(int i=0; i<=3; i++) {
		int x1=x+nex[i];
		int y1=y+ney[i];
		if(x1>=1&&x1<=n&&y1>=1&&y1<=m) {
			if(s[x1][y1]=='1') {
				res++;
			}
			if(s[x1][y1]=='2') {
				flag=1;
			}
		}
	}
	if(res==3&&!flag) {
		return 1;
	} else {
		return 0;
	}
}
int main() {
	cin>>n>>m;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			cin>>s[i][j];
		}
	}
	int res1=0;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			if(s[i][j]=='0') {
				if(check(i,j)) {
					res1++;
				}
			}
		}
	}
	if(res1)
	{
		  cout<<"YES"<<endl;
		  cout<<res1<<endl; 
	}
	else
	{
		 cout<<"NO"<<endl;
	}
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值