【海量集训&&题解】复赛前夕 day3——考试题解

专辑:海量集训->题解

一、绿巨人的跳跃
题目描述
绿巨人想跳上一座高 H 米的大楼, 但跳跃要消耗一定的愤怒值。
这栋大楼每一层高 h 米, 即距地面 0 米, h 米, 2h 米…到 H 米, 都有
一层地板可供绿巨人落脚。 保证 h 是 H 的因数。
绿巨人有两种跳跃方式: 每次跳 h1 米, 一次消耗 w1 的愤怒值; 每次
跳 h2 米, 一次消耗 w2 的愤怒值。从头到尾绿巨人只能选择一种跳跃方式。
绿巨人不能在半空中再次起跳, 所以必须落在离跳跃最高点最近且在最高
点下方的楼层上。 绿巨人需要保证每次跳跃之后愤怒值都是非负的。
现在绿巨人愤怒值为 0, 正在补充愤怒值。 他希望你告诉他至少要补
充多少愤怒值才能跳上这栋大楼。


【 输入格式(jump.in)】
输入一行包含 6 个正整数: H,h,h1,w1,h2,w2。
【 输出格式(jump.out)】
输出一行包含 1 个整数: 绿巨人至少要补充的愤怒值。 如果绿巨人永
远跳不上这栋大楼, 输出“ Puny Hulk” ( 不包含引号) 。


【 样例输入 1】
4 2 2 3 3 4
【 样例输出 1】
6
【 样例输入 2】
10000 4 2 1 4 10000
【 样例输出 2】
25000000
【 样例输入 3】
200 200 1 1 2 1
【 样例输出 3】
Puny Hulk


【 解释】
样例 1 中选择第一种跳跃方式, 跳 2 次可跳上楼顶, 花费 6。 样例 2
中选择第二种跳跃方式, 跳 2500 次可跳上楼顶, 花费 25000000。 样例 3
中由于两种跳跃方式都小于楼层高度, 所以绿巨人无法跳上楼顶。
【 数据规模与约定】
保证 H 是 h 的倍数。
10%的数据保证绿巨人无法跳上楼顶。
另外 30%的数据保证绿巨人两种跳跃方式都能一次跳上楼顶。
另外 30%的数据保证 h1 和 h2 都是 h 的倍数。
100%的数据保证 1<=H,h,h1,w1,h2,w2<=10000


分析:
【 数据一】
直接输出 -1 即可。
【 数据二】
因为一次足够, 所以取 w1 和 w2 的最小值输出。
【 数据三】
专门给不会上取整的小盆友的分, 详见数据四。
【 数据四】
根据题意, 对一种方式, 每跳一次能够跳上的楼层数是相等的, 所以
可以把跳的高度直接当成跳的楼层数。 这一步是一次除法。 之后算总的步
数显然也是一次除法, 复杂度是 O(1)的,
答案为:
m i n ( ( H / h ) / m i n ( ( h 1 / h ) , H / h ) w 1 , ( H / h ) / m i n ( ( h 2 / h ) , H / h ) w 2 ) ( h 1 &gt; = h , h 2 &gt; = h ) min((H/h)/min((h1/h),H/h)*w1,(H/h)/min((h2/h),H/h)*w2) (h1&gt;=h ,h2&gt;=h)
如果h1<h或者h2<h只要取用其中一个值即可

如果两个都小于h就输出那个奇奇怪怪的东西

代码:

#include<bits/stdc++.h>
using namespace std;
#define MAXX 100000000
int minn=MAXX;
int H,h,h1,w1,h2,w2;
int main(){
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
    scanf("%d %d %d %d %d %d",&H,&h,&h1,&w1,&h2,&w2);
    if (h1>=h)
      minn=min(minn,(H/h)/min((h1/h),H/h)*w1);
    if (h2>=h)
      minn=min(minn,(H/h)/min((h2/h),H/h)*w2);
    if (minn==MAXX) printf("Puny Hulk");
    else printf("%d",minn);
    fclose(stdin);
    fclose(stdout);
    return 0;
}


二、雷神的锤子
题目描述
雷神正在一片平原上挥舞它质量为﹢∞的锤子。 除了质量奇特, 还有
一点奇特, 就是它每次砸向地面, 形成的坑都是矩形。
雷神共用锤子砸了 n 次, 每次的力度不一定相同。 我们用 d 代表力度
的大小, 表示锤子砸下去后, 形成的坑的深度是 d。 开始平原上所有地方
的深度都是 0。 如果此次砸下的区域有地方已经被砸过了, 那么这部分的
深度等于各次砸下的 d 的最大值。 假设砸下去只会影响对应矩形的深度,
不会影响矩形外的平原。
现在他派你去把砸出的所有的坑都填上, 你需要先告诉他把坑填上,
也就是使所有地方的深度都是 0, 需要他提供多少单位体积的土。


【 输入格式(hammer.in)】
输入第一行包含一个正整数 n。
接下来 n 行, 每行 5 个整数描述一次砸下, x0,y0,x1,y1,d。 (x0,y0)为矩形
的左下角, (x1,y1)为矩形的右上角。
【 输出格式(hammer.out)】
输出一行包含 1 个整数: 需要多少单位体积的土。


【 样例输入 1】
10
0 3 3 1
【 样例输出 1】
9
【 样例输入 2】
30
0 1 1 4
0 0 1 1 5
0 0 2 2 3
【 样例输出 2】
14


【 解释】
样例 1 中形成一个 331 的坑。
样例 2 中(0,0),(1,1)对应的矩形区域共被砸了 3 次, 深度为 3 次 d 的最
大值 5。
【 数据规模与约定】
保证 x0<=x1,y0<=y1。
30%的数据保证 n=1。
另外 30%的数据保证所有的 d 都相等。
100%的数据保证|x0|,|y0|,|x1|,|y1|<=300,n<=500,1<=d<=109。


分析:
【 数据一】
n=1, 直接算出答案即可。
【 数据二】
因为所有的 d 都相等, 所以都可以视为 1。 我们把平面上可能用到的
点分成 600600 个 11 的正方形。这样对每次砸下都更新所有影响到的点,
最后统计影响到的个数。
【 数据三】
在数据二的基础上把标记改为“当前深度” , 然后对每次砸下更新所
有点的深度,如果<d 则令它等于 d。最后把深度相加。复杂度是 O(300^2*n)。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define plus 301
LL a[701][701];
LL n;
LL ans=0;
int main(){
	freopen("hammer.in","r",stdin);
	freopen("hammer.out","w",stdout);
    scanf("%lld",&n);
    for (LL i=1;i<=n;i++){
	    LL x,y,xx,yy,d;
	    scanf("%lld %lld %lld %lld %lld",&x,&y,&xx,&yy,&d);
	    for (LL i=x+plus;i<xx+plus;i++)
	      for (LL j=y+plus;j<yy+plus;j++)
	        a[i][j]=max(a[i][j],d);
	}
	for (LL i=0;i<=700;i++)
	  for (LL j=0;j<=700;j++)
	    ans+=a[i][j];
	printf("%lld",ans);
	return 0;
}

备注:为什么有负数啊啊啊啊!


三、鹰眼的箭
题目描述
鹰眼用自己超强的视力, 看到了平面上的 n 个敌方火力点, 第 i 个点
的坐标是(Xi,Yi)。 他需要用自己的百发百中的箭把这些火力点摧毁。
鹰眼的箭非常强大, 射一次箭可以把飞行路线路线上所有的火力点直
接摧毁。 假设每支箭的飞行路线都是射线。 鹰眼可以在平面上任意位置射
箭, 但是由于一些原因, 鹰眼的箭的方向只能平行于 x 轴或 y 轴或与某一
坐标轴成 45° 角。
现在鹰眼想知道, 如果只能从一个位置射箭, 不限射出多少支箭, 能
够摧毁火力点最多有多少个。 共有 q 个查询, 每个查询互不干扰。 如果鹰
眼所在位置正好有一个火力点, 无论往哪个方向射箭, 这个火力点都会被
摧毁。 为了减少读写时间, 你需要按照下列公式求出最后输出的值, 其中
的 表示的是异或( C 和 C++中是^, Pascal 中是 xor) 。 设第 i 次询问的正
确答案是 ansi, 那么你需要输出的是:
(ans+1 )^(ans2 +2)^ … ^(ansq ^q)


【 输入格式(arrow.in)】
输入第一行包含 2 个正整数: n 和 q。
接下来 n 行, 第 i 行包含 2 个整数: Xi 和 Yi。
接下来 q 行, 第 i 行包含 2 个正整数: QXi 和 QYi, 代表一次查询, 鹰
眼所在位置是(QXi,QYi)。
【 输出格式(arrow.out)】
输出 1 行表示所有查询结果的异或和。


【 样例输入 1】
4 2
0 0
2 2
0 2
2 0
0 1
1 1
【 样例输出 1】
5
【 样例输入 2】
1 1
233333 233333
233333 233333
【 样例输出 2】
2


【 解释】
下页图表示样例 1 的点分布情况, 不同颜色的箭头表示不同查询能够摧毁
的火力点。 第一次查询结果为 2, 第二次为 4, 所以应输出(2+1)^ (4+2)=5。
在这里插入图片描述
【 数据规模与约定】
保证所有火力点都不重合
30%的数据保证 n,q<=1000。
70%的数据保证坐标|Xi|,|Yi|,|QXi|,|QYi|<=100000。
100%的数据保证|Xi|,|Yi|,|QXi|,|QYi|<=109, n,q<=100000。
【 提示】
输入数据规模较大, 建议避免使用速度慢的输入方式, 如 cin。

分析:【 数据一】
沿 x 轴方向射箭, 摧毁的是 x 值相同的点。 沿 y 轴方向射箭, 摧毁的
是 y 值相同的点。 沿斜方向射箭, 摧毁的是 x+y 相同的点或 x-y 相同的点。
暴力枚举每个点, 看是否 x, y, x+y, x-y 中有其一和给定位置相同, 复杂
度 O(nq)。
【 数据二】
在数据一的基础上, 把枚举去除, 改为统计每个不同的 x 值、 y 值、
x+y 值、 x-y 值。 这样一次查询即为求 4 个统计的数的和。 由于坐标范围不
大, 数组开到 410^5 即可。 注意要判断查询是否与给定的某个点重合,
如果重合了, 这个点将会被算 4 次, 所以答案要减去 3。 这一步可以用排
序或者 STL 中的 set。
【 数据三】
坐标范围变大, 数组不再可用, 这时注意到统计的值只有 O(n)个, 所
以把数组改为 map 或者用排序+二分的方法, 就可以轻松统计个数。 复杂
度为 O(n
logn)。

代码:


#include<bits/stdc++.h>
using namespace std;
int n,m;
int ans=0;
map <pair < int,int >,bool> a;
map <int,int> b;
map <int,int> c;
map <int,int> d;
map <int,int> e;
int main(){
    freopen("arrow.in","r",stdin);
    freopen("arrow.out","w",stdout);
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++){
	    int x,y;
	    scanf("%d %d",&x,&y);
	    b[x]++;
	    c[y]++;
	    d[x+y]++;
	    e[x-y]++;
	    a[make_pair(x,y)]=1;
	}
	for (int i=1;i<=m;i++){
	    int x,y;
	    scanf("%d %d",&x,&y);
	    int sum=0;
	    if (a[make_pair(x,y)]) sum=-3;
	    sum+=b[x]+c[y]+d[x+y]+e[x-y];
	    if (i==1) ans=sum+1;
	    else ans^=sum+i;
	}
	cout<<ans;
	return 0;
}

备注:为什么有负数啊啊啊啊!
四、超级英雄的战斗
题目描述
美国队长和钢铁侠正在与很多外星人战斗。 这场战斗的持续时间是 t
分钟, 每分钟美国队长和钢铁侠都可能消灭 0 个或 1 个外星人。 我们用一
个长度为 t 的字符串 S 来描述这场战斗, 每个字符代表一分钟内发生的情
况: M 表示只有美国队长消灭了 1 个外星人, G 表示只有钢铁侠消灭了 1
个外星人, T 表示两人同时消灭了 1 个外星人, 不存在某 1 分钟两人都没
有消灭外星人。
钢铁侠知道一般情况下他们两个的战斗能力比是 m:g, 表示在相同时
间内如果美国队长消灭了 mk 个敌人, 钢铁侠应当能消灭 gk 个。 战斗之
后, 钢铁侠发现, 并不是所有连续时间段都符合一般情况的。 用 S 的一个
子串 S2=S[l…r]表示一个连续时间段, 我们称 S2 是“好” 的, 当且仅当在
第 l 分钟到第 r 分钟( 两端均包含) 美国队长消灭的敌人数和钢铁侠消灭
的敌人数之比恰好等于 m:g。
钢铁侠需要计算最长的“好” 的 S 的非空子串, 最长的“ 好” 的子串
的数量, 以及 S 的所有“好” 的非空子串的数量。 如果字符串一样但位置
不同分开计算数量。


【 输入格式(fight.in)】
输入第一行包含 3 个非负整数: t,m 和 g。
第二行包含一个长度为 t 的字符串 S, 含义如前所说。
【 输出格式(fight.out)】
输出两行。 第一行包含 3 个整数 l,r 和 cnt, 表示 S 的子串 S[l…r]是最
长的“好” 子串( S 的编号从 1 开始) , 如果有多个输出最左边的子串,
以及与它等长的“ 好”的子串的数量。 第二行包含 1 个整数, 表示 S 的“好”
的非空子串数量。 如果不存在“好” 的非空子串, 只输出”A weird fight”( 不
包含引号) 。


【 样例输入 1】
6 1 2
GTGGTT
【 样例输出 1】
1 6 1
6
【 样例输入 2】
2 3 2
TMG
【 样例输出 2】
A weird fight


【 解释】
样例 1 中, GT,TG,GGTT,TGGT,GTGGTT 都是“ 好” 的子串, 其中 GT 出现
了 2 次, 所以总的个数是 6。
【 数据规模与约定】
保证 m 和 g 至少有一个非 0。 不保证 m:g 是最简比。
保证 S 中的字符只会是 M,G,T 之一。
数据可能有 3 种特殊限定之一。
限定 1: S 只出现 1 种字母。
限定 2: m*g=0
限定 3: S 中没有字符’T’, 且所有的’M’都在所有的’G’之前。
测试点编号 t 的上界 特殊限定
1 100 1
2~5 100 无
6 2000 2
7 2000 3
8~12 2000 无
13,14 100000 2
15,16 100000 3
17~20 100000 无
保证 m,g<=109。


分析:【 特殊限定 1】
S 中的字母只有一个, 那么任意子串对应的 m:g 都是相同的。 如果 S
中只有 M 就是 1:0, 只有 G 就是 0:1, 只有 T 就是 1:1。 因此, 如果题目中
的 m 和 g 的比不等于对应的值, 答案就是“ A weird fight” 。 如果等于对
应值, 那么任何非空子串都是“好” 的, 因此最长的是 S[1…n], 总的个数
是 n
(n+1)/2。
【 特殊限定 2】
不妨假设 m=0, 那么如果子串包含 M 或 T, 一定不是“好” 的。 相反
地, 如果子串只包含 G, 一定是“好” 的。 所以我们可以在 S 中算出所有
连续的 G, 对应地按照特殊限定 1 的做法算出来, 统计答案也很简单。
【 特殊限定 3】
如果 mg=0 即为限定 2, 所以考虑 m>0,g>0。 第一步先把 m:g 化简,
两边同时除以 gcd(m,g)。 统计 S 中 M 的个数和 G 的个数, 那么 S 的“好”
子串一定是 m
k 个字符 M 加上 gk 个字符 G。通过统计可以算出 k 的上限,
这样最长子串便可算出。 显然所有“好” 的子串都不会相同, 所以最长子
串只有 1 个, 总个数就是 k 的上限。
【 t<=100】
暴力枚举子串, 暴力统计其中美国队长和钢铁侠分别消灭了多少敌
人, 然后判断两个的比值是否等于 m:g。 注意这一步不需要 m:g 是最简比,
也不需要求最大公约数, 否则复杂度会加上 log。 设两人消灭的敌人数是
m1:g1, 那么 m1:g1=m:g 当且仅当 m1
g=g1*m, 这样用一次 long long 的乘
法就可判断。 复杂度是 O(n^3)。
【 t<=2000】
定义 Pre_m(i)表示前 i 分钟美国队长消灭的敌人数, Pre_g(i)表示钢铁
侠消灭的敌人数。 可以在线性时间内算出这两个数组。 这样, 枚举子串之
后不需要暴力统计, 设枚举的子串是 S[l…r], 那么 Pre_m( r )-Pre_m(l-1)就表
示这段时间内美国队长消灭的敌人数, 同理 Pre_g( r )-Pre_g(l-1)表示钢铁侠
消灭的敌人数。 然后套用 t<=100 的方法即可, 总的复杂度是 O(n^2)。
【 t<=100000】
我们把判断的式子写下:
g ( p r e m r p r e m l 1 ) = m ( p r e g r p r e g l 1 ) g( pre _ m*r - pre _ m*(l-1)) = m( pre _ g*r - pre _ g*(l-1))
移项之后可以变成:
m p r e g l 1 g p r e m l 1 = m p r e g r g p r e m r m* pre _ g*(l-1) - g* pre _ m*(l-1) = m* pre _ g*r - g* pre _ m*r
可以发现左右两边的式子几乎一样, 而且一个和 r 有关, 一个和 l-1 有关。 定义
新数组f(i)=m * pre_g(i)-g * pre_m(i), 这样 S[l…r]是“好” 的子串的条件就转
化为 f( r )=f(l-1)! 注意到这是一个非常简单的条件, 把 f 不同的值分开统计
即可。 复杂度是 O(nlogn)。 Std 的做法比较简单, 从左到右扫一遍字符串,
对不同的 f 值用 STL 中的 map 记录第一次出现的位置, 以及出现的次数,
这样记录下来就可以做到 O(nlogn), 相对于第一种方法, 实现比较简单,
速度也不慢。
*

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
map <LL,int> First;
map <LL,int> num;
int n,m,g;
LL gtx[1000001];
LL mgdz[1000001];
int maxl=0,maxr=0,sum=0,ans=0,maxlen=0;
int main(){
	freopen("fight.in","r",stdin);
	freopen("fight.out","w",stdout);
    scanf("%d %d %d",&n,&m,&g);
    First[0]=num[0]=1;
    for (int i=1;i<=n;i++){
	    char ch;
	    ch=getchar();
	    while (ch!='T'&&ch!='G'&&ch!='M') ch=getchar();
	    gtx[i]=gtx[i-1]+(ch=='T'||ch=='G');
	    mgdz[i]=mgdz[i-1]+(ch=='T'||ch=='M');
	}
	for (int i=1;i<=n;i++){
	    LL x=m*gtx[i]-g*mgdz[i];
	    if (num[x]){
		    int l=First[x];
		    sum+=num[x];
		    if (i-l+1>maxlen){
			    maxlen=i-l+1;
			    ans=1;
			    maxl=l,maxr=i;
			}
			else if (i-l+1==maxlen)ans++;
			num[x]++;
		}
		else First[x]=i+1,num[x]=1;//因为是l-1,所以右移一位 
	}
	if (!ans) printf("A weird fight");
	else printf("%d %d %d\n%d",maxl,maxr,ans,sum);
	return 0;
}

备注:暴力分很足

总结:题目给我看清楚了,变量名给我用清楚了,文件名给我写清楚了,方位数组给我定清楚,数据规模给我看清楚了啊啊啊

猜你喜欢

转载自blog.csdn.net/huang_ke_hai/article/details/83690864
今日推荐