【2020暑期海亮集训】Day 5:普及组模拟赛(7.21)

目录

    • 引子
    • 题目
      • T1-时间(time)
        • 【题目描述】
        • 【输入数据】
        • 【输出数据】
        • 【样例输入 1】
        • 【样例输出 1】
        • 【样例输入 2】
        • 【样例输出 2】
        • 【样例输入 3】
        • 【样例输出 3】
        • 【数据范围】
        • 【附加知识】
        • 【解题思路】
        • 【Code】
      • T3-数学(math)
        • 【题目描述】
        • 【输入数据】
        • 【输出数据】
        • 【样例输入】
        • 【样例输出】
        • 【数据范围】
        • 【解题思路】
        • 【Code】
      • T4-发际线(hair)
        • 【题目描述】
        • 【输入数据】
        • 【输出数据】
        • 【样例输入】
        • 【样例输出】
        • 【数据范围】
        • 【解题思路】
        • 【Code】
    • 备注

引子

怎么还有10天结束这恐怖的集训呀!整天只有考试,考试,考试?
又是一套模拟赛,还是综合的,一套普及组模拟赛?!

题目

T1-时间(time)

【题目描述】

小罗是时间管理大师,但他最近想去国外恰饭,你能帮助他将一个具体的时
间翻译成英文吗,如果不存在这样的日期,请输出“frog”(不含引号)。
英文日期一般为:月份 日期,年份
例如 2020 年 7 月 18 日,英文为:July 18, 2020。(注意逗号后面有空格)

【输入数据】

输入的第一行一个 8 位数字串

【输出数据】

如果存在,输出日期的英文翻译,不存在,输出“frog”。

【样例输入 1】

20200718

【样例输出 1】

July 18, 2020

【样例输入 2】

20210229

【样例输出 2】

frog

【样例输入 3】

00010101

【样例输出 3】

January 1, 1

【数据范围】

对于 10% 的数据, 日期不存在
对于 30% 的数据,日期在 2020 年 7 月
对于 80% 的数据,日期在 10000101 以后
对于 100% 的数据,日期在公元后

【附加知识】

月份 英文简写 英文全称
一月 Jan. January
二月 Feb. February
三月 Mar. March
四月 Apr. April
五月 May. May
六月 Jun. June
七月 Jul. July
八月 Aug. August
九月 Sept. September
十月 Oct. October
十一月 Nov. November
十二月 Dec. December

【解题思路】

  • 水水的签到题。
  • 个人感觉这种题好像没有什么好说的,月份英文名和每个月天数都塞到数组里,判断一下非法状态,不非法就直接输出,就这样水过了…

【Code】

#include 
using namespace std;
string s;
string yuefen[13]={"!","January","February","March","April","May","June","July","August","September","October","November","December"};
int tianshu[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
void Fre()
{
	freopen("time.in","r",stdin);
	freopen("time.out","w",stdout);
}
int main()
{
	Fre();
	ios::sync_with_stdio(false);
	cin >> s;
	int year=0,month=0,day=0;
	for (int i=0;i<=3;i++) year=year*10+s[i]-'0';
	for (int i=4;i<=5;i++) month=month*10+s[i]-'0';
	for (int i=6;i<=7;i++) day=day*10+s[i]-'0';
	//算出字符串中年,月,日
	if (month==2)
	{
		bool p=0;
		if ((year%4==0 && year%100!=0) || year%400==0)  //当前是闰年
		{
			p=1;
		}
		if (p==1) tianshu[2]=29;  //是闰年
		if (p==0 && day==29)  //不是闰年你二月还29天!?
		{
			cout << "frog";
			return 0;
		}
	}
	if (month>12) cout << "frog";  //月份非法
	 else
	if (day>tianshu[month]) cout << "frog";  //天数非法
	 else
	{
		cout << yuefen[month] << ' ' << day << ", " << year;
	}
	return 0;
}

T3-数学(math)

(原题:洛谷 P1284 三角形牧场)

【题目描述】

小 B 最近在学数学,他有 n 个线段,第 i 个线段长度为 li,他想知道用这些
线段围成一个三角形,面积最大是多少。

【输入数据】

输入的第 1 行:一个整数 n;
第 2 行,n 个整数,第 i 个整数表示 li

【输出数据】

仅一个数:最大牧场面积(保留两位小数)。如果无法构建,输出 -1。

【样例输入】

5
1 1 3 3 4

【样例输出】

6.93

【数据范围】

对于 60% 的数据,保证 n≤10,li≤20。
对于 100% 的数据,保证 3≤n≤40,1≤li≤40。

【解题思路】

  • 这道题用DP来做,因为这道题全部线段都要用上,而且知道了线段总和,第一条边长度和第二条边长度,就可以得出第三条边的长度,所以可以设一个状态: f [ i ] [ j ] f[i][j] f[i][j] 表示第一条边为 i i i,第二条边为 j j j 是否能够由某几条线段拼出来(为0就是不可行,为1就是可行)
  • 然后就是状态转移方程了,对于一个状态 f [ i ] [ j ] f[i][j] f[i][j] 来说,如果 f [ i − l [ k ] ] [ j ] f[i-l[k]][j] f[il[k]][j] 可行或者 f [ i ] [ j − l [ k ] ] f[i][j-l[k]] f[i][jl[k]] 可行,是不是就说明 f [ i ] [ j ] f[i][j] f[i][j] 可以由 f [ i − l [ k ] ] [ j ] f[i-l[k]][j] f[il[k]][j] 或者 f [ i ] [ j − l [ k ] ] f[i][j-l[k]] f[i][jl[k]] 得来?貌似是的。然后如果当前状态是可行的,那么我们去判断它的三条边是否能组成一个三角形,运用海伦公式求出三角形面积,求出最大值 a n s ans ans 就可以了。

【Code】

#include 
using namespace std;
bool f[2020][2020];
//f[i][j] 表示第一条边为i,第二条边为j是否能拼凑得出来(1表示可以,0表示不可以)
int l[45];
double S(int A,int B,int C)  //海伦公式求三角形面积
{
	double a=1.0*A,b=1.0*B,c=1.0*C;
	double p=(a+b+c)/2.0;
	return sqrt(p*(p-a)*(p-b)*(p-c));
}
bool check(int a,int b,int c)
{
	return a+b>c && a+c>b && b+c>a;
}
void Fre()
{
	freopen("math.in","r",stdin);
	freopen("math.out","w",stdout);
}
int main()
{
	Fre();
	int n;
	scanf("%d",&n);
	int sum=0;
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&l[i]);
		sum+=l[i];  //所有线段长度之和 
	}
	double ans=0;
	int s=0;
	f[0][0]=1;
	for (int k=1;k<=n;k++)
	{
		s+=l[k];  //当前i,j两条边的长度大小不会超过前k条线段之和 
		for (int i=s;i>=0;i--)
		{
			for (int j=s;j>=0;j--)
			{
				bool f1=(i>=l[k] && f[i-l[k]][j]);
				bool f2=(j>=l[k] && f[i][j-l[k]]);
				if (f1 || f2)  //两种条件只要有一种符合就说明两条边为i,j的这种情况可以由i-l[k],j的第一条边加上当前枚举的边 或 i,j-l[k]的第二条边加上当前枚举的边得到 
				{
					f[i][j]=1;  //当前情况可以由某几条线段拼出 
					if (check(i,j,sum-i-j))  //当前的三条边符合三角形的性质 
					{
						ans=max(ans,S(i,j,sum-i-j));  //答案取最优值 
					}
				}
			}
		}
	}
	if (ans==0) printf("-1");
	printf("%.2lf",ans);
	return 0;
}

T4-发际线(hair)

【题目描述】

小 B 最近在学数学,他头发有点凉,由于最近发际线有点高,他的头发排列
成了整齐的一排。
小 B 有 n 根头发,第 i 根头发高度为 li(0<=li<=n)。理想情况下,他想要他
的头发在长度上单调递增,所以他定义他的头发的“不良度”为逆序对的数量:满
足 i< j 及 li > lj 的二元对 (i,j)。
现在他去剪头发了,他想知道,对于每个 j,当他长度大于 j 的头发都被剪成
j 以后,头发的“不良度”为多少。
(有趣的事实:人类平均确实有大约 10^5 根头发!)

【输入数据】

输入的第 1 行:一个整数 n;
第 2 行,n 个整数,第 i 个整数表示 li

【输出数据】

对于每一个 j=0,1,…,N-1,用一行输出不良度。

【样例输入】

5
5 2 3 3 0

【样例输出】

0
4
4
5
7

【数据范围】

对于 20% 的数据,保证 n<=100。
对于 50% 的数据,保证 n<=2,000。
对于 100% 的数据,保证 1<=n<=100,000。

【解题思路】

  • 这几乎是一个板子,树状数组求逆序对。
  • 前置技能:树状数组。
  • 每次读入一个高度,当然要先+1(因为待会儿做树状数组的时候 l o w b i t ( 0 ) = 0 lowbit(0)=0 lowbit(0)=0 就无限死循环了…)
  • 然后就把这个高度丢进树状数组,因为高度 x x x 在树状数组中是用来做下标的,所以当前做一个 getsum(x),就是在原高度序列中在第 i i i 个高度之前塞进树状数组且高度比 x x x 小的数量,那么用 i − g e t s u m ( x ) i-getsum(x) igetsum(x) ,就是在原序列中在当前这个高度之前塞进树状数组且高度比 x x x 大的数量,用 f [ x + 1 ] f[x+1] f[x+1] 表示当高度为 x + 1 x+1 x+1 的出现时, x x x 贡献的逆序对数量,最后把 f f f 数组累加起来就可以了。(每个高度 j j j 出现时,将 j − 1 j-1 j1 贡献的逆序对数量累加到答案中)

【Code】

#include 
using namespace std;
int c[200020],f[100010];
const int N=200010;
int lowbit(int x)
{
	return x&(-x);
}
int getsum(int x)
{
	int ans=0;
	for (;x;x-=lowbit(x)) ans+=c[x];
	return ans;
}
void add(int x,int y)
{
	for (;x<=N;x+=lowbit(x)) c[x]+=y;
}
void Fre()
{
	freopen("hair.in","r",stdin);
	freopen("hair.out","w",stdout);
}
int main()
{
	Fre();
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		x++;  //因为lowbit(0)=0所以要加一
		add(x,1);  //把x扔到树状数组里 
		f[x+1]+=i-getsum(x);  //f[x+1]为当高度为x+1的出现时,x贡献的逆序对数量
	}
	int ans=0;
	for (int j=1;j<=n;j++)
	{
		ans+=f[j];
		printf("%d\n",ans);
	}
	return 0;
}

备注

这套题显然是有一道 T 2 T2 T2 的,然而由于码量比较大,而且数据有锅,就不放了(其实是本蒟蒻不能保证自己305行的程序是能 A C AC AC 的…)

你可能感兴趣的:(集训的日子)