防线题解。

防线

题目描述

 lsp 学习数学竞赛的时候受尽了同仁们的鄙视,终于有一天......受尽屈辱的 

lsp 黑化成为了黑暗英雄Lord lsp。就如同中二漫画的情节一样,Lord lsp 
打算毁掉这个世界。数学竞赛界的精英 lqr 打算阻止Lord lsp 的阴谋于
是她集合了一支由数学 竞赛选手组成的超级行动队。由于队员们个个都
智商超群,很快,行动队便来到了Lord lsp的黑暗城堡的下方。

但是,同样强大的 Lord lsp 在城堡周围布置了一条“不可越过”的坚固防线。防线由很多
防具组成,这些防具分成了 N 组。我们可以认为防线是一维的,那么每一组防具都分布 
在防线的某一段上,并且同一组防具是等距离排列的。也就是说,我们可以用三个整数 
S, E 和 D 来描述一组防具,即这一组防具布置在防线的 S,S + D,S + 2D,...,S + 
KD(K∈ Z,S + KD≤E,S + (K + 1)D>E)位置上。

黑化的 Lord lsp 设计的防线极其精良。如果防线的某个位置有偶数个防具,那么这个位 
置就是毫无破绽的(包括这个位置一个防具也没有的情况,因为 0 也是偶数)。只有有奇
数 个防具的位置有破绽,但是整条防线上也最多只有一个位置有奇数个防具。作为行动队的队 长,lqr 要找到防线的破绽以策划下一步的行动。但是,由于防具的数量太多,她
实在是不 能看出哪里有破绽。作为 lqr 可以信任的学弟学妹们,你们要帮助她解决这个
问题。

输入格式

输入文件的第一行是一个整数 T,表示有 T 组互相独立的测试数据。 每组
数据的第一行是一个整数 N。 之后 N 行,每行三个整数 Si,Ei,Di,代表
第 i 组防具的三个参数。

输出格式

对于每组测试数据,如果防线没有破绽,即所有的位置都有偶数个防具,输出一行 	
“There's no weakness.”(不包含引号)

否则在一行内输出两个空格分隔的整数 P 和 C,表示在位置 P 有 C 个防具。当然 C 应 
该是一个奇数。

样例输入

3
2
1 10 1
2 10 1
2
1 10 1 
1 10 1 
4
1 10 1 
4 4 1 
1 5 1 
6 10 1

样例输出

1 1
There's no weakness.
4 3

数据范围与提示

对于 30% 的数据,满足防具总数不多于 10^7。

对于 100% 的数据,满足防具总数不多于 10^8,Si≤Ei,1≤T≤5,N≤200000,0≤Si, 			
Ei,Di≤2^31-1。
注: 0 ≤ S i , E i , D i ≤ 231 − 1 0≤Si,Ei,Di≤231−1 0SiEiDi2311 是写错了的,应该是 0 ≤ S i , E i , D i ≤ 2 31 − 1 0≤Si,Ei,Di≤2^{31} - 1 0SiEiDi2311

什么东东, 2 31 − 1 2^{31}−1 2311,这不玩我吗,再看“防具总数不多于 1 0 8 10^8 108”,哦,我懂了, 这道题可以不做 不能简单的用暴力了,怎么办捏???

首先我们先来 瞎搞 给你举个栗子,假设,这条防线上的防具数量是这样子的:

防线上的数 2 4 3 6 8 10(注意题目,最多只有一个奇数,这也是解决本题的要点)

前缀和的值 2 6 9 15 23 33

我们先来打一个暴力的代码,再康康前缀和

在这里插入图片描述

你会发现一个惊奇的事情:

1.自打过了奇数行以来,前缀和就独有奇数

2.如果没有奇数的话,那么前缀和也没有奇数

辣么,我们是不是可以用前缀和来找到这个只有奇数个防具的行呢?

ps:这个规律的证明真不难,不想打这么多字了

我们再想一想:如何求前缀和?

注意:前缀和就是前面所有的包括这一行的防具的和并不一定要用标准的:pre[i] = pre[i - 1] + a[i].

我们再仔细读题,发现另一个重要的事情,就是在每组数据当中,布置了防具的行数按顺序排列起来竟是一个等差数列,辣么,知道首项,末项和公差,我们就可以轻松的求出在x行之前的防具之和了,再跑一遍,不就得出答案了吗?

暴力枚举每一行的防具之和很明显会爆炸,所以我们采用二分 (这点应该容易想到吧)

我们再计算一下用时会不会超
在这里插入图片描述
快看!不会捏,最多不能超过1e9这个差远了拿qwq,( •̀ ω •́ )y。

注:雷老师说最好不能超过1e8,出了锅与我无瓜qwq

思路就是这样, 代码高亮

#include <cstdio>

const int INF = 0x3f3f3f3f;
const int MAXN = 200005;
int t, n;
int l[MAXN], r[MAXN], tol[MAXN];//l,r,tol分别表示题目中的s,e,d 

int f(int, int);//二分,判断mid指向的是不是有奇数个防具 
int _Cal(int);//计算在x行之前的防具数量 
int _min(int, int);//手撸求min值,因为郭老师说用系统的容易费掉 

int main() {
	scanf("%d", &t); 
	for(int i = 1; i <= t; i++) {
		scanf("%d", &n);
		int L = INF, R = -INF;//用来找首尾 
		for(int j = 1; j <= n; j++) {
			scanf("%d %d %d", &l[j], &r[j], &tol[j]);
			if(l[j] < L) L = l[j];//首要是最小的 
			if(r[j] > R) R = r[j];//尾要是最大的 
		}
		int pos = f(L, R);//二分查找答案 
		if(pos == -1) printf("There's no weakness.\n");//没有行有奇数个防具 
		else printf("%d %d\n", pos, _Cal(pos) - _Cal(pos - 1));//有某一行有奇数个防具 
		//注意:pre[i] - pre[i - 1]的值就是这一行的防具数量 
	}
	return 0;
}

int f(int l, int r) {
	
//	printf("%4d %4d\n", l, r);
//	printf("%d\n", _Cal(l));
	
	if(l == r) {//第一种出口 
		if(_Cal(l) % 2 == 1) return l;
		else return -1;//没有行有偶数个防具 
	}
	if(l + 1 == r) {//第二种出口 
		if(_Cal(l) % 2 == 1) return l;
		else if(_Cal(r) % 2 == 1) return r;
		else return -1;//没有行有偶数个防具 
	}
	int mid = (l + r) / 2;
	if(_Cal(mid) % 2 == 1) return f(l, mid);//如果这里的前缀和是奇数,辣么有奇数个防具的那一行在前面或mid指的这里 
	else return f(mid, r);//如果这里的前缀和是偶数,辣么有奇数个防具的那一行在后面
}

int _Cal(int x) {//计算前缀和 
	int sum = 0;
	for(int i = 1; i <= n; i++) {
		if(x - l[i] < 0) continue;//如果最小的一行都比这里大,辣么ta是肯定不会影响前缀和的 
		sum += (_min(x, r[i]) - l[i]) / tol[i] + 1;
		//1.一定要+1,因为l[i]这里会有一个防具 
		//2.一定要求最小值,因为可能x并不在l[i]~r[i]里面 
	}
	return sum;
}

int _min(int x, int y) {//不想多说qwq 
	if(x < y) return x;
	else return y;
}
ps:上次符号三角形的代码写的太丑,让LJS大佬 喷了 提出了改进的方法,这次我用了百度翻译,应该还是比较标准了吧qwq ,同时,在这里也感谢LJS大佬的帮助

小结一下:这道题让我懂了个道理:在你不会时,暴力是最好的办法,试试几个数据,找找规律,一道看似不可能的题就会对呢qwq

模仿刘洋大大: 好累啊,给个赞呗qwq。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值