长沙学院2021校赛

长沙学院2021校赛

一、小圆前辈去上学

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/A
来源:牛客网

题目描述:

小圆前辈刚上小学一年级,开学第一天老师就讲了对于小数如何四舍五入成整数。
例如:4.78四舍五入就是5,3.11四舍五入就是3。
现在老师让她回家写个程序, 如何将小数四舍五入成整数。

输入描述:
给你一个正实数n。

输出描述:
输出一个整数表示四舍五入后的结果。

2.解题思路

考虑到浮点数长度小于等于2000,所有可以用string来存输入的数。然后可以for循环遍历字符串,找到整数部分和小数点后一位,小数点后一位大于等于5则整数进位。整数可用long long进行存储,但是需要特判是否存在小数点。

3.参考代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const double PI=acos(-1.0);
int main()
{
	string s;
	cin>>s;
	int x=-1;
	ll ans=0;
	for(int i=0;i<s.length();i++)
	{
		if(s[i]=='.'){
			x=i;
			break;
		}
		ans=ans*10+(s[i]-'0');
	}
	if(x==-1)cout<<ans<<endl;
	else
	{
		int f=s[x+1]-'0';
		if(f>=0&&f<=4)cout<<ans<<endl;
		else cout<<(ans+1)<<endl;
	}
}

二、小圆前辈的素数

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/B
来源:牛客网

题目描述:

小焰同学由于在大学的时候不学无术,所学的专业知识不足矣她找到一份像样的工作,这让她很苦恼。之后的某一天她发现,中国几乎所有的科技公司都宣布开始造车,所以她觉得以后汽车维修工作这样的人才缺口一定很大,从此她发奋图强,拜师学艺,在一年后的今天当上了一名光荣的汽车维修师傅。今天是小焰同学上班的第一天,她需要修理的是一辆拥有中控异响、刹车失灵、充电异常、地库自燃等等问题的一台TSL汽车。虽然这种车况本应直接报废要求厂家退款的,但是官方说这是车主自身的问题,不予以质保。乐于助人的小焰同学决定帮一帮这位韭菜。经过问题排查后,她得到了汽车的两组电线接头,一组接头个数为 n ,另一组接头个数为 m 。每个接头都有一个数值显示,她需要将两边的接头每边选择一个进行对接,对接之后两边的数值会进行相加,如果结果为质数的话证明接对了,否则就接错了。如果仅仅是为了接对,她自己就可以解决了,但是好学的她想要知道有多少种正确的对接方式,不学无术的她因为以前训练不饱和做不来,所以请你帮帮她。

输入描述:
第一行输入一个整数t,代表样例组数。 (t ≤ 5)
对于每一组样例中:
第一行输入两个整数 n m (1≤ n, m ≤ 100000)
第二行输入 n 个整数表示一边的接头示数
第三行输入 m 个整数表示另一边的接头示数
示数的范围 [1, 100000]

输出描述:
输出一个整数,表示答案。

这题就是判断a与b数组中存在多少对ai+bj为素数。

2.解题思路

FFT我也不会呀!

三、小圆前辈去爬山

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/C
来源:牛客网

题目描述:

今天是周末,天气很好?小圆前辈决定去爬山。她来到了一个有n座山的地方,每座山都有一个编号a,和高度h。由于她是一个,坚持不懈、自强不息、全力以赴、百折不挠、斗志昂扬、持之以恒、雄心壮志……的人,所以她只会爬比她所在的山高的山且比她当前山的编号大的山,如果她从编号为i的山爬到编号为j的山。(hi < hj, i < j),那么她将消耗hj*(j - i + 1)的体力值。当她的体力值小于当前消耗的体力值则无法爬山。
现在有q次询问,每次询问当小圆前辈在 编号为x的山,有w的体力值,能到达的山中编号最大的是多少?

输入描述:
第一行两个整数 n,q 表示有n座山,q次询问。
接下来有n行,每行有两个整数a,h表示山的编号,和编号为a的山的高度。
接下来有q行,每行有两个整数x,w表示小圆前辈在编号为x的山上,拥有的体力值为w。

输出描述:
有q行,对于每次询问输出一个整数表示能到达的山中编号最大的。

(这题是真不会,我是fw)

四、小圆前辈的魔法

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/D
来源:牛客网

题目描述:

小焰同学很害怕虫子,每次看到会飞的昆虫也会跟着飞起来,为此小圆前辈就决定用魔法在寝室里面划定一个区域,让这个区域保持一个无虫的状态,小圆前辈施法过程是这样的:悬停在俯视图为三角形的寝室上空,画上一条边界经过寝室,将寝室分为两个部分,这样较小的区域就会成为一个没有任何虫子的区域,因为小圆前辈的室友更多是喜欢昆虫的。小圆前辈魔法虽然不能划出好友指定大小的区域,但是可以保证的是边界一定会将寝室划分成两个面积大于0的区域。现在请你计算最终小圆前辈能分出多大的区域给好友。

输入描述:
前三行每行两个整型数,代表三角形寝室的坐标
第四行四个整型数,代表边界的坐标

输出描述:
输出一行浮点数,代表小圆前辈好友得到的无虫区域面积
输出与答案误差不超过 1e-6 视为正确答案

2.解题思路

一条直线可将三角形分割成两种类型:直线过三角形一个顶点和与两条边相交。(即以下六种情况),对应每一种情况可直接套板子(叉积求三角形面积,点和直线关系,直线和直线交点)
在这里插入图片描述

3.参考代码

#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
int sgn(double x)
{
	if (fabs(x) < eps)return 0;
	else return x < 0 ? -1 : 1;
}
struct Point {
	double x, y;
	Point(){}
	Point(double x,double y):x(x),y(y){}
	Point operator + (Point B) { return Point(x + B.x, y + B.y); }
	Point operator - (Point B) { return Point(x - B.x, y - B.y); }
	Point operator * (double k) { return Point(x * k, y * k); }
	Point operator / (double k) { return Point(x / k, y / k); }
};
struct Line {
	Point p1, p2;
	Line(){}
	Line(Point p1, Point p2):p1(p1),p2(p2){}
};
double xmult(Point p1, Point p2, Point p3)
{
	return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
}
double Cross(Point A, Point B)//叉积
{
	return A.x * B.y - A.y * B.x;
}
double Area(Point A, Point B, Point C)//三角形面积*2
{
	return Cross(B - A, C - A);
}
int Point_line_relation(Point p, Line v)//点和直线位置关系
{
	int c = sgn(Cross(p - v.p1, v.p2 - v.p1));
	if (c == 0) return 0;//p在v上
	else return 1;
}
Point Cross_point(Point a, Point b, Point c, Point d)
{
	double s1 = Cross(b - a, c - a);
	double s2 = Cross(b - a, d - a);
	return Point(c.x * s2 - d.x * s1, c.y * s2 - d.y * s1) / (s2 - s1);
}
int main()
{
	Point a, b, c;
	Line v;
	scanf("%lf%lf%lf%lf%lf%lf", &a.x, &a.y, &b.x, &b.y, &c.x, &c.y);
	scanf("%lf%lf%lf%lf", &v.p1.x, &v.p1.y, &v.p2.x, &v.p2.y);
	double area = fabs(Area(a, b, c) / 2.0);
	if (Point_line_relation(a, v) == 0)
	{
		Point f = Cross_point(v.p1, v.p2, b, c);
		double minn = fabs(Area(a, b, f) / 2.0);
		printf("%.10lf\n", min(minn, area - minn));
	}
	else if (Point_line_relation(b, v) == 0)
	{
		Point f = Cross_point(v.p1, v.p2, a, c);
		double minn = fabs(Area(a, b, f) / 2.0);
		printf("%.10lf\n", min(minn, area - minn));
	}
	else if (Point_line_relation(c, v)==0)
	{
		Point f = Cross_point(v.p1, v.p2, a, b);
		double minn = fabs(Area(a, c, f) / 2.0);
		printf("%.10lf\n", min(minn, area - minn));
	}
	else
	{
		if (xmult(b, v.p1, v.p2) * xmult(c, v.p1, v.p2) > eps)
		{
			Point f1, f2;
			f1= Cross_point(v.p1, v.p2, a, b);
			f2= Cross_point(v.p1, v.p2, a, c);
			double minn = fabs(Area(a, f1, f2) / 2.0);
			printf("%.10lf\n", min(minn, area - minn));
		}
		else
		{
			if (xmult(a, v.p1, v.p2) * xmult(c, v.p1, v.p2) > eps)
			{
				Point f1, f2;
				f1 = Cross_point(v.p1, v.p2, a, b);
				f2 = Cross_point(v.p1, v.p2, b, c);
				double minn = fabs(Area(b, f1, f2) / 2.0);
				printf("%.10lf\n", min(minn, area - minn));
			}
			else if(xmult(a, v.p1, v.p2) * xmult(b, v.p1, v.p2) > eps)
			{
				Point f1, f2;
				f1 = Cross_point(v.p1, v.p2, a, c);
				f2 = Cross_point(v.p1, v.p2, b, c);
				double minn = fabs(Area(c, f1, f2) / 2.0);
				printf("%.10lf\n", min(minn, area - minn));
			}
		}
	}
}

五、小圆前辈的排列组合

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/E
来源:牛客网

题目描述:

小焰同学是小圆前辈的好朋友,她已经在CCSU生活三年了,别看她现在是在ACM实验室,其实在之前她一直忙于各种社团与班级团建活动之中,还在兼顾学习成绩与竞赛强度的她有一些力不从心,尤其是大一的时候因为社团的节目排练还占用了她大部分时间,甚至影响了正常作息,让她在接下来的大学生活一直处于阴影之中。作为她的室友因为长期收到这样的负面情绪而苦不堪言。所以为了让她训练饱和,想要帮她在之后的日子中去除那些琐碎的杂事,只挑有意义的事情让她做,现在我们把所有的事件比作一个长度为 n 的的字符串,每一件事情是一个字符。她在第合数个事件中想要划水看番打游戏,所以你只能从素数事件中挑选事件来让她做,其中如果在所有挑选的事件中可以重新排列组合成为“ CCSU ” 的话,证明她做的事情是有意义的,那么请问在这一系列的事情中她是否可以做有意义的事呢,可以的话输出 “Yes”,否则输出 “No”。(字符串从一开始)

输入描述:
仅包含一行字符串 s
每个字符可能为数字或者大小写字母

输出描述:
如果小焰同学可以做有意义的事情
输出 “Yes”
否则输出 “No”(不带引号)

2.解题思路

可先用欧拉筛筛出字符串长度内的所有素数,然后遍历prime[i]数组,记录相对应发字符,当‘C‘字符出现大于等于2、’S’和‘U’字符出现大于等于1则可输出Yes,到最后还未找到则输出No

3.参考代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const double PI=acos(-1.0);
const int maxn=1e6+10;
int prime[maxn];
bool vis[maxn];
int cnt;
void Prime()
{
	for(int i=2;i<=maxn;i++)
	{
		if(vis[i]==false)prime[cnt++]=i;
		for(int j=0;i*prime[j]<=maxn;j++)
		{
			vis[i*prime[j]]=true;
			if(i%prime[j]==0)break;
		}
	}
}
int main()
{
	string s;
	cin>>s;
	map<char,int>m;
	Prime();
	prime[0]=2;
	int flag=0;
	for(int i=0;i<cnt;i++)
	{
		if(i>s.length())break;
		m[s[prime[i]-1]]++;
		if(m['C']>=2&&m['S']>=1&&m['U']>=1)
		{
			flag=1;
			break;
		}
	}
	if(flag==1)cout<<"Yes\n";
	else cout<<"No\n";
}

六、小圆前辈的数组Ⅱ

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/F
来源:牛客网

题目描述:

在你的帮助下,小圆前辈成功破译了这个长为n的数组。原来这个数组是小焰同学上周送给她的,并安排小圆前辈帮她算出数组中的最长完美子序列的长度,可是粗心的小圆前辈忘记了。小圆前辈现在再一个一个找已经来不及了,于是便求助于你,你能帮她算出最长完美子序列的长度吗?

我们定义一个序列是完美的:对于所有的1≤ i ≤ n,满足b[i]不是b[i + 1]的因数。

子序列的定义:a是 b的子序列, 当且仅当可以从b中删除一些元素得到a。
输入描述:
第一行只有三个整数n。

第一行共n个整数a[1]~a[n]。

输出描述:
一个整形数表示答案。

2.解题思路

这明显是一道DP题,在比赛中我想着用求最长上升子序列的线性模板+贪心来套用,但实现起来不是一般的难,赛后看了大佬的代码,一个字“绝”。
首先可用dp[i]来表示在数组选择第i个值时前面能组成的长度,但是在不优化的条件下你在找到前面的数不是a[i]的因子且dp值最大的情况一定会超时。
对于1e5以下的所有数的因子不超过100,同时我们在dp转移时只需要知道最大值即可,所以可用set里面存一个pair,其中first拿来存取dp值,second存取对应的下标,因为要找最大的dp值,我们可以将set对first进行从大到小排序;
对于每一个a[i]都需要遍历一遍set,当里面存在一个下标对应的值不是a[i]的因子时,只需要将dp[i]进行更新。如果a[i]值在前面没有出现过的话,则需要将它加入到set中去。当出现过的话,如果目前a[i]的dp值比前一个大,则需要将前一个值从set中删除,然后添加新的值即可,最后对dp取max即可。

3.参考代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
#define P pair<int,int>
int n;
int a[maxn], dp[maxn], vis[maxn];
set<P, greater<P>>s;//first记录dp值,second记录位置,dp值从大到小
int ans;
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	fill(dp, dp + maxn, 1);
	for (int i = 1; i <= n; i++)
	{
		for (auto it : s)
		{
			if (a[i] % a[it.second] != 0)
			{
				dp[i] = it.first + 1;
				break;
			}
		}
		if (vis[a[i]] == 0)
		{
			s.insert({ dp[i],i });
			vis[a[i]] = i;
		}
		else
		{
			if (dp[vis[a[i]]] < dp[i])
			{
				s.erase({ dp[vis[a[i]]] ,vis[a[i]] });
				s.insert({ dp[i], i });
				vis[a[i]] = i;
			}
		}
		ans = max(ans, dp[i]);
	}
	printf("%d\n", ans);
}

七、小圆前辈的数组

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/G
来源:牛客网

题目描述:

小圆前辈最近收到了一个长度为n数组。她怀疑是不怀好意的魔女给她的陷阱,于是她对数组进行了剖析后发现了两个关键的整数k和z,而解读此数组只要算出的所有连续子序列中有多少满足:
1,所有数的和为k的倍数;
2,且其和至少为z;
这个问题难到了小圆前辈,她便把这个问题交给了你,如果你能帮她解决的话,她将奖励你一个Accept。

输入描述:
第一行只有三个整数n,k,z。
第一行共n个整数a[1]~a[n]。

输出描述:
一个整形数表示答案。

2.解题思路

前缀和。我们可以开一个二维数组来存储前缀和%k的值相等对应的前缀和值。在遍历前缀和时,我们只需要操作前缀和大于等于z的值。在操作时,我们需要找到sum[i]%k==0时sum[i]所需要减去数的个数,所以我们只需要访问i=sum[i]%k的这个数组,在这个数组中查找有多少个数被sum[i]减去后还大于等于z,但是暴力的话可能会超时。所以我们可以二分找到第一个数x(x满足sum[i]-x<z),则这个数之前的数全满足条件。(也可用upper_bound())

不会upper_bound的点此处

3.参考代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5 + 10;
ll n, k, z;
ll a[maxn];
ll sum[maxn];
vector<ll>v[maxn];
int main()
{
	scanf("%lld%lld%lld", &n, &k, &z);
	ll ans = 0;
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &a[i]);
		sum[i] = sum[i - 1] + a[i];
		v[sum[i] % k].push_back(sum[i]);
	}
	for (int i = 1; i <= n; i++)
	{
		if (sum[i] < z)continue;
		if (sum[i] % k == 0)ans++;
		ll zz = sum[i] % k;
		ll ff = sum[i] - z;
		ans += upper_bound(v[zz].begin(), v[zz].end(), ff) - v[zz].begin();
	}
	printf("%lld\n", ans);
}

八、小圆前辈的博弈

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/H
来源:牛客网

题目描述

小圆前辈今天将小焰同学叫来了,她们在玩一个有趣的游戏。小圆前辈有一个长度为n的字符串S,小焰有一个长度为m的字符串T,游戏规则是这样的:首先小圆前辈从S中取出一个子串s,然后小焰从T中取出一个子串t,若s与t相等的话,小圆前辈就输了,否则小焰输。小圆前辈想知道她有多少种必胜的取法,并向你求助,你能告诉她吗?
对于取出的任意的两个子串,只要在原串中位置不同我们就认为是不同的取法。

例如:abab中1-2的ab与3-4的ab虽然子串一样,但我们认为是不同的取法。

输入描述:
第一行两个整数n,m分别表示S的长度与T的长度。
第二行一个字符串S。
第三行一个字符串T。

输出描述:
一个整数表示答案。

2.解题思路

刚开始想着直接开map<string,int>来记录第二个字符串的所有字串,然后遍历第一个字符串的子串,当map里面对应的值为0时,则ans++。但经过自己的不懈努力,终于内存超限了。
对于字符串,我又想到用字符串hash来实现,map里面也变成了long long和int ,又经过修改题交,好样的又TLE了。map其实查询的时候比较费时,所以又采用了unordered_map<ll, int>mm来优化。

3.参考代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn=31;
int n, m;
string a, b;
int ans;
unordered_map<ll, int>mm;
int main()
{
	cin >> n >> m;
	cin >> a >> b;
    for(int i=0;i<m;i++)
    {
        ll s=0;
        for(int j=i;j<m;j++)
        {
            s=(s*maxn+(b[j]-'0'));
            mm[s]++;
        }
    }
    for(int i=0;i<n;i++)
    {
        ll s=0;
        for(int j=i;j<n;j++)
        {
           s=(s*maxn+(a[j]-'0'));
            if(mm[s]==0)
            {
                 ans+=n-j;
                 break;
            }
        }
    }
    cout<<ans<<endl;
}

九、小圆前辈的暴力枚举

1.题目

链接:https://ac.nowcoder.com/acm/contest/15332/I
来源:牛客网

题目描述

小圆前辈家有一个n * m的矩阵,对于矩阵上的每一个格子,你都可以放置一个棋子(易知总共有2^{n *m}种放置情况)。小圆前辈想知道,在所有放置情况中有多少种情况满足:对于矩阵的每一行且每一列至多只有1个棋子。小圆前辈一想,这不是暴力枚举一下就行了,于是便把问题交给你了,你能求出答案是多少吗。

结果对998244353取模。

输入描述:
一行只有两个整数,n和m。

输出描述:
一个整形数表示答案。

2.解题思路

排列组合问题。因为一行和一列最多一个棋子,所以我们最多可以放min(n,m)个棋子,对于放x个棋子,我们可以选择在n行中选x行,在m列中选k列,则总共有C(n,x)*A(m,x)种放法。

3.参考代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const double PI = acos(-1.0);
const ll M = 998244353;
const int N = 1007;
ll ans = 0;
ll fac[N + 10];
ll A(ll x, ll y)
{
	ll sum = 1;
	for (ll i = y; i >= y - x + 1; i--)
		sum = (sum * i) % M;
	return sum % M;
}
ll ksm(ll a, ll b)
{
	ll ans = 1;
	while (b)
	{
		if (b & 1)ans = (ans * a) % M;
		b >>= 1;
		a = (a * a) % M;
	}
	return ans;
}
void init()
{
	fac[0] = 1;
	for (int i = 1; i <= N; i++)
		fac[i] = (fac[i - 1] * i) % M;
}
ll inv(ll x) {
	return ksm(x, M - 2);
}
ll C(ll n, ll m) 
{
	return fac[n] * inv(fac[m]) % M * inv(fac[n - m]) % M;
}
int main()
{
	ll n, m;
	init();
	scanf("%lld%lld", &n, &m);
	ans = (n * m + 1) % M;
	ll y = min(n, m);
	for (int i = 2; i <= y; i++)
	{
		ll f = C(n, i) % M;
		ll xx = A(i, m);
		ans = (ans + (f * xx) % M) % M;
	}
	printf("%lld\n", ans);
}

十、小圆前辈的异或树

不会不会,我是菜鸡!

十一、小圆前辈的888

只会输入,老菜了…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值