区间找最值的二分与前缀和

《1.题目一--最佳牛栏问题》

题目链接:https://www.acwing.com/problem/content/104/

用到的基本思想:二分,前缀和,平均值的特殊处理

这个题目的题意是,给出N块连续的田地,田地上有牛;
我们想要在这个给出的一系列田地中找出连续的田地(连续的意思是按照题目给出的如 1 2 3 4 5 6 72 3 4 是连续的,而1 3 5不是连续的)上牛数的最大平均值
1.由这里N《=1e5,应该想到用二分找答案,然后去判断这个二分出来的答案对不对;
于是题目变成了,我找到了答案mid,是否能够找到一段平均值》=mid;
如果找到了那么还有提升空间l=mid+1;
否则 r=mid;
2.那么如何去判断这个最大平均值呢?
在一个区间上求值,第一个想到的是就是前缀和;
但是这里是平均值,有除数;
可以让这题目给出的数值都-mid,然后转化为前缀和新得的,看某一区间是否》=0
如果》=0,那么说明这一段区间》=mid;
3.当然这个是有长度要求的,即区间长度》=f;
我们可以开一个变量minv,一直维护其最小
由sum[k]-sum[minv]可以得到最大的
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 const int N = 1e5 + 10;
 6 int n, f;
 7 double sum[N];
 8 double num[N];
 9 bool check(double mid)
10 {
11     for (int i = 1; i <= n; i++)
12         sum[i] = sum[i - 1] + (num[i]-mid);
13     double minv=0;
14     for (int i=f;i<=n;i++)
15     {
16         minv=min(minv,sum[i-f]);
17         if (sum[i]-minv>=0) return true;
18     }
19     return false;
20 }
21 int main()
22 {
23     scanf("%d%d", &n, &f);
24     for (int i = 1; i <= n; i++)
25         scanf("%lf", &num[i]);
26     double l = 0, r = 2000;
27     while (r - l > 1e-6)
28     {
29         double mid = (l + r) / 2;
30         if (check(mid))
31             l = mid;
32         else
33             r = mid;
34     }
35     printf("%d",int(r*1000));
36     return 0;
37 }

《2.题目2--防线》

题目链接:https://www.acwing.com/problem/content/description/122/
120. 防线
达达学习数学竞赛的时候受尽了同仁们的鄙视,终于有一天......受尽屈辱的达达黑化成为了黑暗英雄怪兽达达。

就如同中二漫画的情节一样,怪兽达达打算毁掉这个世界。

数学竞赛界的精英 lqr 打算阻止怪兽达达的阴谋,于是她集合了一支由数学竞赛选手组成的超级行动队。

由于队员们个个都智商超群,很快,行动队便来到了怪兽达达的黑暗城堡的下方。

但是,同样强大的怪兽达达在城堡周围布置了一条“不可越过”的坚固防线。

防线由很多防具组成,这些防具分成了 N 组。

我们可以认为防线是一维的,那么每一组防具都分布在防线的某一段上,并且同一组防具是等距离排列的。

也就是说,我们可以用三个整数 S, E 和 D 来描述一组防具,即这一组防具布置在防线的 S,S+D,S+2D,…,S+KD(K∈Z,S+KD≤E,S+(K+1)D>E)位置上。

黑化的怪兽达达设计的防线极其精良。

如果防线的某个位置有偶数个防具,那么这个位置就是毫无破绽的(包括这个位置一个防具也没有的情况,因为 0 也是偶数)。

只有有奇数个防具的位置有破绽,但是整条防线上也最多只有一个位置有奇数个防具。

作为行动队的队长,lqr 要找到防线的破绽以策划下一步的行动。

但是,由于防具的数量太多,她实在是不能看出哪里有破绽。

作为 lqr 可以信任的学弟学妹们,你们要帮助她解决这个问题。

输入格式
输入文件的第一行是一个整数 T,表示有 T 组互相独立的测试数据。

每组数据的第一行是一个整数 N。

之后 N 行,每行三个整数 Si,Ei,Di,代表第 i 组防具的三个参数,数据用空格隔开。

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

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

数据范围
防具总数不多于108,

Si≤Ei,

1≤T≤5,

N≤200000,

0≤Si,Ei,Di≤2311
输入样例:
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
思路是利用只有一个奇数,
1.只有一个奇数说明,我可以利用二分法l=0,r=0x3f3f3f3f;
去枚举在哪里;
因为如果那个奇数在l-mid中,则l-mid这个区间的防具总和为奇数,
反之是奇数在mid+1-r;
2.如何求l-mid中的防具总和呢?
前缀和
我们算0-mid与0-(l-1)的防具总和,相减为l-mid这个区间的防具总和;
如何求0-x中的防具总和呢?
遍历每一组,注意我们是求0-x中的防具总和,是很容易的
for (int i=1;i<=n;i++){
if (x<si) ans+=0;
else ans+=(min(x,ei)-si)/di+1;
}
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 const int N = 1e5 + 10;
 6 struct node
 7 {
 8     int s, e, d;
 9 } zhu[N];
10 int t, n;
11 long long sum(int x)
12 {
13     long long ans = 0;
14     for (int i = 1; i <= n; i++)
15     {
16         if (x < zhu[i].s)
17             ans += 0;
18         else
19         {
20             ans += (min(x, zhu[i].e) - zhu[i].s) / zhu[i].d + 1;
21         }
22     }
23     return ans;
24 }
25 bool check(int l, int mid)
26 {
27     if ((sum(mid) - sum(l - 1)) % 2)
28         return true;
29     else
30         return false;
31 }
32 int main()
33 {
34     scanf("%d", &t);
35     while (t--)
36     {
37         scanf("%d", &n);
38         int l = 0, r = 1e9;
39         for (int i = 1; i <= n; i++)
40         {
41             scanf("%d%d%d", &zhu[i].s, &zhu[i].e, &zhu[i].d);
42             l = min(zhu[i].s, l);
43             r = max(zhu[i].e, r);
44         }
45         while (r > l)
46         {
47             int mid = (l + r) >> 1;
48             if (check(l, mid))
49                 r = mid;
50             else
51                 l = mid + 1;
52         }
53         long long ans = sum(r) - sum(r - 1);
54         if (ans % 2)
55         {
56             printf("%d %lld\n", r, ans);
57         }
58         else
59         {
60             printf("There's no weakness.\n");
61         }
62     }
63     return 0;
64 }这个代码有问题是过不了的,但是思路是对的

 

posted @ 2022-05-29 20:37  次林梦叶  阅读(69)  评论(0)    收藏  举报