区间找最值的二分与前缀和
《1.题目一--最佳牛栏问题》
题目链接:https://www.acwing.com/problem/content/104/
、
用到的基本思想:二分,前缀和,平均值的特殊处理 这个题目的题意是,给出N块连续的田地,田地上有牛; 我们想要在这个给出的一系列田地中找出连续的田地(连续的意思是按照题目给出的如 1 2 3 4 5 6 7 ,2 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≤231−1 输入样例: 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 }这个代码有问题是过不了的,但是思路是对的

浙公网安备 33010602011771号