NEFU大一集训总结赛题解

概述

整体难度还行思维性不是很高,好在寒假刷题的时候开阔了一些眼界所以大致上还算顺利(可能前几天被牛客的集训杀爆了吧hh)

最后一个多小时挣扎了一下A题感觉搞不出来就先到这里写一下题解(当然不是最优解啦,大概讲讲解法,emm部分题目如果有需要的话可以这篇博客评论区里留个言,我可以专门写博客来分析
比赛链接

知识点

题目知识点
A-SETgcd
B-密码解锁快速幂,打表
C-夕日坂双指针,尺取法
D-小林的AC模拟
E-小林刷题贪心
F-小林移石子中位数,排序
G-语汐玩游戏逆序对,dfs,打表
H-字符串之谜字符串,dp
I-小林取数贪心

题解

A-SET

分析

选取a和b,进行2a-b操作
先模拟一波,集合中只有两个数,我们假定a>b
那么会出来……2b-a,b,a,2a-b……发现貌似是一个公差为a-b的等差数列
也就是说,我们选两个数,就可以衍生出公差为两数之差的等差数列
现在我们加入c,那么想想一个轴先被a-b划分为等间距的,然后a-b会被划分成c-b和a-c两段,其中大的那段又会被小的那段划分,一直进行下去直到所有段相等,停止划分。也就是说最后会变成一个等差数列,那么他们的公差是多少呢?答案是起始所有相邻段长度的最大公约数(因为你想啊,c-b啊,a-c啊这种每一段都要能被某一长度均分,那不就是公约数码,而且他一旦被均分就停止了,那就取最大呗)
所以我们排个序,把a1作为起点,然后求一下所有相邻段长度的最大公约数就能用下面这个式子把所有能产生的数表示出来了
x = a [ 1 ] + m ∗ g c d ( a [ 2 ] − a [ 1 ] , a [ 3 ] − a [ 2 ] … … ) x=a[1]+m*gcd(a[2]-a[1],a[3]-a[2]……) x=a[1]+mgcd(a[2]a[1],a[3]a[2]
我们令g为最大公约数那么k需要能表示为
k = a [ 1 ] + m g k=a[1]+mg k=a[1]+mg
在这里插入图片描述

代码

  #include <bits/stdc++.h>
  using namespace std;
  typedef long long LL;
  const int MAX=2e5+11;
  int n;
  LL a[MAX];
  LL gcd(LL a,LL b){
  	if(b==0)return a;
  	return gcd(b,a%b);
  }
  int main(){
  	int T;
  	scanf("%d",&T);
  	while(T--){
  		LL k;
  		scanf("%d%lld",&n,&k);
  		for(int i=1;i<=n;++i){
  			scanf("%lld",&a[i]);
  		}
  		sort(a+1,a+n+1);
  		LL g=0;
  		for(int i=2;i<=n;++i){
  			g=gcd(g,a[i]-a[i-1]);
  		}
  		if((a[1]-k)%g==0){
  			printf("YES\n");
  		}
  		else{
  			printf("NO\n");
  		}
  	}
  	return 0;
  }

标称Hack数据(感谢ph巨佬)

所有数都一样gcd会变成0,无法取模
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAX=2e5+11;
int n;
LL a[MAX];
LL gcd(LL a,LL b){
if(b==0)return a;
return gcd(b,a%b);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
	LL k;
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;++i){
		scanf("%lld",&a[i]);
	}
	sort(a+1,a+n+1);
	LL g=0;
	for(int i=2;i<=n;++i){
		g=gcd(g,a[i]-a[i-1]);
	}
	if(a[1]==k)puts("YES");
	else 
	{
		if(g&&(a[1]-k)%g==0)puts("YES");
		else puts("NO");
	}
}
return 0;
}

B-密码解锁

分析

快速幂+打表
一般这种题目吧,要么数学推导,要么打表找规律
我这种蒟蒻当然是打表找规律啦
数学推导的话(应该可以用欧拉降幂吧,不过我不咋会就打表了)
打表发现基本都是前面一段不循环,后面一段循环的
写的时候标记开始循环的点和循环节的长度即可利用取模运算减小复杂度啦

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long//long long 缩写
const int N=1e5+10;
ll read()//快速读入,你们直接cin或者scanf即可
{
  ll x=0,f=0;char ch=getchar();
  while(!isdigit(ch))f|=ch=='-',ch=getchar();
  while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
  return f?-x:x;
}
ll fp(ll a,ll b,ll mod)//快速幂
{
    ll res=1;a=a%mod;
    while(b)
    { 
        if(b&1)res=(res*a)%mod;
        a=(a*a)%mod;b>>=1;
    }
    return res;
}

ll T,Q,n,m,k,cnt,tmp;//常用变量组
ll ans[N];
bool tj[N];
int main()
{
	int a=read();k=read();
	while(!tj[a])
	{
		tj[a]=1;
		ans[++cnt]=a;
		a=fp(a,a,1000);
	}
	int st;//标记开始循环位置
	for(int i=1;i<=cnt;i++)
		if(a==ans[i])st=i;
	int t=(k-st)%(cnt-st+1);
	cout<<ans[st+t];
   	return 0;
}

C-夕日坂

分析

连续乘积不超过m
双指针呗,还好我写过博客,先前发群里了如果看了的话应该可以A出这题吧
就是博客里第1题反过来写即可,不过貌似不能前缀积因为太大了,用tmp模拟即可(如果可以的话给俺点个赞?)
双指针尺取法小结

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;
ll read() 
{
  ll x=0,f=0;char ch=getchar();
  while(!isdigit(ch))f|=ch=='-',ch=getchar();
  while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
  return f?-x:x;
}
ll T,Q,n,m,k,cnt,tmp;
ll a[N],ans[N];
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)a[n-i+1]=read();
	tmp=1;
	for(int i=1,j=1;i<=n;i++)
	{
		tmp*=a[i];
		while(j<=i&&tmp>m)
		{
			tmp/=a[j];
			j++;
		}
		ans[i]=i-j+1;
	}
	for(int i=n;i>=1;i--)
	{
		if(i==n)printf("%lld",ans[n]);
		else printf(" %lld",ans[i]);
	}
   	return 0;
}

D-小林的AC

分析

如果你刷林大oj的话应该记得一道西红柿炒鸡蛋吧,短板效应,统计最少的即可,e和c有两个要特殊处理

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF=0x3f3f3f3f;
ll T,Q,n,m,k,p,ans,cnt,sum,tmp;
unordered_map<char,int>mp;
string s;
int main()
{
	while(cin>>s)
	{
		int minx=INF;
		mp.clear(); 
		for(int i=0;s[i];i++)mp[s[i]]++;
		for(auto t:mp)
		{
			char x=t.first;
			int y=t.second;
			if(x=='c'||x=='e')minx=min(minx,y/2);
			else if(x=='a'||x=='p'||x=='t'||x=='d')minx=min(minx,y);
		}
		printf("%lld\n",minx);
	}
   	return 0;
}

E-小林刷题

分析

比较基础的贪心,按a[i]/b[i]排序即可下附证明
把交换前交换后相邻两项写一下
交换相邻两项的话值影响这两项的值,其他的是不影响的
下面公式里S[i]数后缀和,数组b[i]从第i个到最后的和
做一下简单的化简即可,划线的可以约去
比较 a [ i ] ∗ b [ i + 1 ] ? a [ i + 1 ] ∗ b [ i ] a[i]*b[i+1]?a[i+1]*b[i] a[i]b[i+1]?a[i+1]b[i]即可,最后移项就得到按比值排序
在这里插入图片描述

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10,INF=0x3f3f3f3f;

ll T,Q,n,m,k,ans,cnt,sum,tmp;
struct problem
{
	double a,b,t;
}p[N];
ll s[N];
bool cmp(problem x,problem y)
{
	if(x.t!=y.t)return x.t<y.t;
	return 0;
}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		double a,b,t;
		scanf("%lf%lf",&a,&b);
		t=a/b;
		p[i]={a,b,t};		
	}	
	sort(p+1,p+1+n,cmp);
	for(int i=n;i>=1;i--)s[i]=s[i+1]+p[i].b;
	for(int i=1;i<n;i++)ans+=p[i].a*s[i+1];
	cout<<ans;
   	return 0;
}

F-小林移石子

分析

看到石子脑子里想到堆和区间DP,瞬间紧张。but,这题和这两个没啥关系,货舱选址问题取中位数即可
∑ i = 1 n ∣ a [ i ] − x ∣ \sum_{i=1}^n|a[i]-x| i=1na[i]x要让这个式子最小取中位数就可以啦,高中知识
绝对值求和图像就这两种,分n的奇偶即可,不难证明
在这里插入图片描述

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;

ll read() 
{
  ll x=0,f=0;char ch=getchar();
  while(!isdigit(ch))f|=ch=='-',ch=getchar();
  while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
  return f?-x:x;
}
ll T,Q,n,m,k,p,ans,cnt,sum,tmp;
ll a[N];
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	sort(a+1,a+1+n);
	ans=0;
	for(int i=1;i<=n;i++)ans+=abs(a[i]-a[(n+1)/2]);
	cout<<ans;
   	return 0;
}

G-语汐玩游戏

分析

应该是jsc(啊啊我之前把果光当成jsc学长了,别打我)学长出的题吧dfs/打表应该可做,不过我选择逆序对嘻嘻,其实原型应该是奇数码问题吧
逆序对可用于解决:交换+可达局面(codeforces上也碰到过相关的用逆序对来构造)
直接说结论吧:
奇数码:每次变化逆序对变化偶数个,所以目标和开始的局面逆序对要奇偶相同
偶数码:逆序对之差和两局面下空格所在行数之差奇偶相同

这题偶数码用第二个结论即可
相关推导的话你们可以自己买一本蓝书来看
或者可以看看这篇知乎文章

代码

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

ll T,Q,n,m,k,cnt,tmp;
char g[3][3],need[3][3];
string a,b;
int nxa,nxb,rowa,rowb;//nx为逆序数,row为x所在行
int main()
{
	for(int i=0;i<2;i++)cin>>g[i];
	for(int i=0;i<2;i++)cin>>need[i];
	
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
		{
			if(g[i][j]!='X')a+=g[i][j];
			else  rowa=i;
			if(need[i][j]!='X')b+=need[i][j];
			else rowb=i;
		}
	//计算逆序数
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<i;j++)
		{
			if(a[j]>a[i])nxa++;
			if(b[j]>b[i])nxb++;
		}
	}
	int ans=abs(nxa-nxb)+abs(rowa-rowb);
	if(ans%2==0)printf("YES");
	else printf("NO");
   	return 0;
}

H-字符串之谜

分析

这题训练做过吧,没做出来的话建议反思吧,训练里的是奶牛的,不讲了

代码

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

string s;
int main()
{
	ll a=0,b=0,c=0;
	cin>>s;
	for(int i=0;s[i];i++)
	{
		if(s[i]=='a')a++;
		if(s[i]=='b')b+=a;
		if(s[i]=='c')c+=b;
	}
	cout<<c;
   	return 0;
}

I-小林取数

分析

直接全部取,如果不是10倍数直接输出,是的话看有没有不是10倍数的数,有的话减去
如果全都是10的倍数,那就裂开,直接输出0

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10,INF=0x3f3f3f3f;//INF为一个很大的值你写个1e9+7也可以
ll read() 
{
  ll x=0,f=0;char ch=getchar();
  while(!isdigit(ch))f|=ch=='-',ch=getchar();
  while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
  return f?-x:x;
}

int T,Q,n,m,k,p,ans,cnt,sum,tmp;
int a[N];
int main()
{
	n=read();
	int minx=INF;
	bool flag=1;
	for(int i=1;i<=n;i++)
	{
		a[i]=read();sum+=a[i];
		if(a[i]%10!=0)
		{
			minx=min(minx,a[i]);
			flag=0;
		}
	}
	if(sum%10==0&&!flag)cout<<sum-minx;
	else if(sum%10!=0&&!flag)cout<<sum;
	else if(flag&&sum%10==0)cout<<0;
   	return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值