长沙学院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())
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
只会输入,老菜了…