新生欢乐红包赛题解

A.EASY0
本体考察选手是否会写代码,取模后分类讨论即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int x;
	cin>>x;
	x%=4;
	if(x==1)cout<<"0 A"<<endl;
	if(x==3)cout<<"2 A"<<endl;
	if(x==2)cout<<"1 B"<<endl;
	if(x==0)cout<<"1 A"<<endl;
} 

B.EASY1
麻将题,分类讨论,分成三张同类/两张同类/各不同类三种情况进行考虑。
本体为模拟题,考察选手是否有较为缜密的思维和多阶段的编程能力。建议先在草稿纸上写下思路,分类情况,判断方法,再实现代码。

#include<bits/stdc++.h>
using namespace std;
int a[3];
int main(){
	string s1,s2,s3;
	cin>>s1>>s2>>s3;
	if(s1[1]==s2[1]&&s1[1]==s3[1]){
		a[0]=s1[0];
		a[1]=s2[0];
		a[2]=s3[0];
		sort(a,a+3);
		if(a[0]==a[2])cout<<0<<endl;
		else if(a[0]==a[1]-1&&a[1]==a[2]-1)cout<<0<<endl;
		else if(a[0]==a[1]||a[1]==a[2])cout<<1<<endl;
		else if(a[0]==a[1]-1||a[1]==a[2]-1)cout<<1<<endl;
		else if(a[0]==a[1]-2||a[1]==a[2]-2)cout<<1<<endl;
		else cout<<2<<endl;
	}else{
		if(s1[1]!=s2[1]){
			if(s1[1]==s3[1])swap(s2,s3);
			else if(s2[1]==s3[1])swap(s1,s3);
		}
		if(s1[1]!=s2[1])cout<<2<<endl;
		else{
			a[0]=s1[0];
			a[1]=s2[0];
			sort(a,a+2);
			if(a[0]==a[1]||a[0]==a[1]-1||a[0]==a[1]-2)cout<<1<<endl;
			else cout<<2<<endl;
		}
	}
}

PS:打麻将是ACM中重要的知识点,希望大家有空多打麻将。

C.EASY2

仍然是模拟题,先对当前页上的所有数字进行操作,然后翻页,然后重复上述操作。但是有一个问题需要注意,如果每页上的数字特别少,而页码特别多的话,可能会因为重复的翻页操作导致超时,所有用一个O(1)的技巧直接获得新的页码,使用整数除法+取余:

即:

    LL rmn=lst%k;
	st=(p[c]/k)*k+rmn;
	if(lst<p[c])lst+=k;

另:开longlong。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL p[100005];
LL n,m,k;
LL cnt=0;
LL sv=0;
int main(){
	scanf("%lld%lld%lld",&n,&m,&k);
	for(LL i=0;i<m;++i)scanf("%lld",&p[i]);
	LL lst=k;
	LL c=0;
	while(c<m){
		if(p[c]<=lst){
			++c;
			++sv;
		}else if(sv){
			lst+=sv;
			sv=0;
			++cnt;
		}else{
			LL rmn=lst%k;
			lst=(p[c]/k)*k+rmn;
			if(lst<p[c])lst+=k;
		}
	}
	if(sv)++cnt;
	cout<<cnt<<endl;
}

D.EASY3

和第一题同属不需要动脑的简单题,放在easy组最后一个是为了充分论证“题目难度不按升序排列”。

直接找到最大值,然后每个元素进行比对即可。

我找不到我自己的代码了,所以随便找了一个写的比较清楚的ac代码。

#include<stdio.h>
int main(void)
{
	int n,max=-1,tol=0;
	scanf("%d",&n);
	int a[n-1];
	for(int i=0;i<n;i++)  scanf("%d",&a[i]);
    for(int i=0;i<n;i++)
    {
    	if(a[i]>max)  max=a[i];
	}
	for(int i=0;i<n;i++)
	{
		tol+=max-a[i];
	}
    printf("%d",tol);
}

E。HARD0

题源hdu1356.

https://blog.csdn.net/qq_42778110/article/details/83958457

题解较长,见上链接。需要的基础知识有:基础数论(同余,取模,gcd)

F.HARD1

前置知识:排序

题目意思是,给你一个n元数组( a i &lt; m a_i&lt;m ai<m),每次操作,你可以任选一些元素,使其+1 再对m取模。问至少多少次操作可以使得这个数列单调不减。

先看数据范围,1e5数量级,那么上限是nlogn的算法。不难发现答案满足单调性质,所以我们考虑二分答案,希望设计出O(n)内的check算法。这样的复杂度至少要从左往右扫一遍每个元素,在扫描的过程中,如果左边的元素更小,对于右边的元素来说是严格更优的,这样就满足贪心的性质。所以我们考虑使用贪心法来解决问题。

现在我们假设x次操作可以实现目标,因为有n个元素,那么至少需要从左往右扫一遍。当我们考虑第k个元素 a k a_k ak,事实上 a k a_k ak满足如下性质:
a k ′ ∈ [ a k , a k + x ] i f a k + x &lt; m {a_k}&#x27;∈[a_k,a_k+x] \quad if \quad a_k+x&lt;m ak[ak,ak+x]ifak+x<m
e l s e a k ′ ∈ [ a k , m − 1 ] o r [ 0 , a k + x − m ] else \quad {a_k}&#x27;∈[a_k,m-1]\quad or\quad [0,a_k+x-m] elseak[ak,m1]or[0,ak+xm]
且:
a k &gt; = a k − 1 i f k &gt; 0 a_k &gt;= a_{k-1} \quad if \quad k&gt;0 ak>=ak1ifk>0

那么我们可以先利用第一个公式对每个元素用O(1)时间求出 a k a_k ak的可行域,然后再判断在可行域中最小可以取什么值,这同样可以在O(1)时间内解决。所以最终复杂度就是O(nlogn),满足要求。

在实际编码是,先二分答案,每次二分从左往右扫一边,保存一个pre值表示之前一个数的大小,然后对于每个数求出其可行域,然后在可行域中找到最小的>=pre的值。

代码如下:

/*a[] 表示原数组*/
/*t[]表示如何保存可行域,$a_0,a_1;a_2,a_3$分别保存两个闭区间的端点*/
#include<bits/stdc++.h>
using namespace std;

template <class T>
T read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	x*=f;return x;
}

int n,m; 
int a[300005],pre,t[4];
int l,r;
bool check(int x){
	pre=0;
	for(int i=0;i<n;++i){
		t[0]=t[1]=-1;
		if(a[i]+x>=m){
			t[0]=0;
			t[1]=a[i]+x-m;
		}
		t[2]=a[i];
		t[3]=min(a[i]+x,m-1);
		if(t[0]<=pre&&pre<=t[1])pre=pre;
		else if(t[1]+1<=pre&&pre<=t[2]-1)pre=t[2];
		else if(t[2]<=pre&&pre<=t[3])pre=pre;
		else return 0;
	}
	return 1;
}


int main(){
	read(n),read(m);
	for(int i=0;i<n;++i)read(a[i]);
	l=-1,r=m;
	while(r>l+1){
		int m=(l+r)>>1;
		if(check(m))r=m;
		else l=m;
	}
	cout<<r<<endl;
	return 0;
}

G.HARD2

前置知识:无(甚至不需要会博弈论)

如果先手选手第一步动完不立刻输掉的话,最后所有的石子会变成0-1-2-…-(n-1),因为不能有任何两列相同。所以分两种情况讨论,如果先手第一步不输,那么根据初末状态的奇偶性可以判断;接下来只要考虑第一步直接失败的情况。

我们对石子个数排序,有以下三种情况会暴毙:
1.存在两对或以上相同的石子数(包括三连),这样怎么搞选完都有至少一对相同;
2.存在一对相同的且存在一堆石子比这堆石子刚好少一个,这样选这对相同的就会出现一对新的相同的;
3.存在两个或以上0,这样你随便怎么搞,选完还是有至少两个0。

#include<bits/stdc++.h>
using namespace std;

#define LL long long
int n;
LL a[100005];
int flag=0;

int main(){
	scanf("%d",&n);
	for(int i=0;i<n;++i)scanf("%lld",&a[i]);
	sort(a,a+n);
	for(int i=0;i<n-1;++i){
		if(a[i]==a[i+1]){
			if(i&&(a[i]==a[i-1]||a[i]==a[i-1]+1)){
				cout<<"cslnb"<<endl;
				return 0;
			}
			if(a[i]==0){
				cout<<"cslnb"<<endl;
				return 0;
			}
			if(flag++){
				cout<<"cslnb"<<endl;
				return 0;
			}
		}
	}
	LL sum=0;
	for(int i=0;i<n;++i)sum=(sum+a[i]-i)%2;
	cout<<(sum?"sjfnb":"cslnb")<<endl;
}

H.HARD3

前置知识:线段树

看到这个数据规模应该很容易想到nlogn数据结构按顺序递推logn求取状态参量。

因为向上开口,比较特殊,所以考虑从上到下遍历。借用扫描线的思想,构造一颗单点覆盖区间查询的线段树。

先对数据离散化,这样只要建立一颗1-200000范围的树即可。

然后从上往下,对于一个纵坐标y,我们考虑所有在这个y上出现的点的x值。我们把这些x值全部更新到线段树上,总区间和tree[1]表示到当前y为止,总共有多少个不同的x值出现。那么很显然的, a n s + = ( t r e e [ 1 ] + 1 ) ∗ t r e e [ 1 ] / 2 ans+=(tree[1]+1)*tree[1]/2 ans+=(tree[1]+1)tree[1]/2,也就是所有可能的不同选法。但是要考虑一些其他情况,对于某个区间,如果在当前y上没有新的x值产生(或者自己覆盖自己),那么这个区间其实和之前出现过的区间是一样的,所有不应该被计入答案,所有用线段树查询相邻两个本y出现的x值之间历史x值的个数,然后类似上述公式减去即可。

因为每个点只被插入一次并且产生一次区间查询,所有复杂度 O ( N l o g N ) O(NlogN) O(NlogN)

#include<bits/stdc++.h>
using namespace std;

struct node{
	int x, y;
}a[200005];
int n;
long long ans=0;
int v[200005],top=0;

const int N=200005;
int tree[200006<<2];
//单点修改 
void modify(int x,int k){
	for(x+=N;x;x>>=1)tree[x]+=k;
}
//区间查询
int query(int l,int r){
	if(l>r)return 0;
	int ans=0;
	for(l=N+l-1,r=N+r+1;l^r^1;l>>=1,r>>=1){
		if(~l&1)ans+=tree[l^1];
		if(r&1)ans+=tree[r^1];
	}
	return ans;
} 

bool cmp1(node a,node b){
	return a.x<b.x;
}

bool cmp2(node a,node b){
	if(a.y>b.y)return 1;
	if(a.y<b.y)return 0;
	return (a.x<b.x);
}

long long fun(int x){
	long long r=x;
	r=r*(r+1)/2;
	return r;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d%d",&a[i].x,&a[i].y);
	sort(a+1,a+n+1,cmp1);
	int cnt=1;
	int lst=a[1].x;
	a[1].x=1;
	for(int i=2;i<=n;++i){
		if(a[i].x==lst)a[i].x=cnt;
		else{
			lst=a[i].x;
			a[i].x=++cnt;
		}
	}
	sort(a+1,a+n+1,cmp2);
	for(int i=1;i<=n;){
		int ty=a[i].y;
		top=0;
		while(a[i].y==ty)v[top++]=a[i++].x;
		for(int j=0;j<top;++j)modify(v[j],1-query(v[j],v[j]));
		v[top++]=n+1;
		ans+=fun(tree[1]);
		for(int j=0;j<top-1;++j)ans-=fun(query(v[j]+1,v[j+1]-1));
		ans-=fun(query(1,v[0]-1));
	}
	cout<<ans<<endl;
}

I.HARD4

前置知识:二分查找

这题是显然的正面状态巨多,答案具有单调性,且反面检验非常简单的题。不用说,快乐二分。

我们考虑按照一开始的走法,我们会走出一个向量 a ⃗ = ( x , y ) \vec{a}=(x,y) a =(x,y),同时题目会给你一个坐标 b ⃗ = ( x ′ , y ′ ) \vec{b}=(x&#x27;,y&#x27;) b =(x,y),那么这中间就有一个偏移量 ▽ ⃗ = a ⃗ − b ⃗ \vec{\bigtriangledown} = \vec{a}-\vec{b} =a b ,希望通过修改一些操作,使得这个 ▽ ⃗ \vec{\bigtriangledown} 可以被实现。

那么我们二分答案,记录当前二分到的区间长度为len,那么我们先把这个区间中的所有的操作都撤回,就有一个被撤回的向量 r ⃗ \vec{r} r ,这个向量等于这个区间内操作的和向量,那么就有新的 ▽ ⃗ ′ = ▽ ⃗ − r ⃗ \vec{\bigtriangledown}&#x27; = \vec{\bigtriangledown}-\vec{r} = r 。那么接下来我们可以随便填充这len个数,如果len>=( ▽ ⃗ ′ \vec{\bigtriangledown}&#x27; 的曼哈顿距离)且他们奇偶性相同,那么答案成立,反之答案不成立。由此可以二分得到结果。

#include<iostream>
#include<cstring>
using namespace std;

#define ABS(x) ((x)>=0?(x):-(x))

const int maxn=2e5+7;
int n,tarx,tary;
string s;
int u[maxn],d[maxn],l[maxn],r[maxn];
int delx,dely;

bool check(int len){
	for(int ll=1;ll+len-1<=n;++ll){
		int rr=ll+len-1;
		int du=u[rr]-u[ll-1];
		int dd=d[rr]-d[ll-1];
		int dl=l[rr]-l[ll-1];
		int dr=r[rr]-r[ll-1];
		int dx=delx+dr-dl;
		int dy=dely+du-dd;
		int delta=ABS(dx)+ABS(dy);
		if(delta<=len)return 1;
	}
	return 0;
}

int main()
{
	cin>>n>>s>>tarx>>tary;
	for(int i=1;i<=n;++i){
		u[i]=u[i-1];
		d[i]=d[i-1];
		l[i]=l[i-1];
		r[i]=r[i-1];
		if(s[i-1]=='U')++u[i];
		if(s[i-1]=='D')++d[i];
		if(s[i-1]=='L')++l[i];
		if(s[i-1]=='R')++r[i];
	}
	int nowx=r[n]-l[n],nowy=u[n]-d[n];
	delx=tarx-nowx;
	dely=tary-nowy;
	int lo=-1,hi=n+1;
	while(lo<hi-1){
		int mid=(lo+hi)>>1;
		if(check(mid))hi=mid;
		else lo=mid;
	}
	if((delx+dely)&1)hi=n+1;
	if(hi==n+1)cout<<-1<<endl;
	else cout<<hi<<endl;
		
}

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值