Blog Post Rating CodeForces - 806E (线段树二分)

题目链接

题目大意: 有一个博客, 初始分数为$0$, 有$n$个人, 第$i$个人有一个期望值$a_i$,

如果第$i$个人浏览博客时,博客赞数高于$a_i$博客分数$-1$, 低于$+1$, 相等不变,

对于每个$i$, 求出$[1,i]$的人按任意顺序浏览博客后最大分数.

题解:

首先, 用贪心可以知道所有人按期望升序排列, 最后得分一定最大

由于期望有负数, 博客分数一定是先减后增的, 然后对这两段分类讨论

对于递减的段, 最后分数为递增递减的临界值

假设临界值为$x$, 设比$x$小的数的个数为$cnt_x$

可以发现$x$是第一个满足 $cnt_x+x>=0$ 的$x$

比方说排序后序列为$-10,-10,-10,-6,-2,-1$ 那么$x$就为$-4$

权值线段树上二分即可求出$x$ (初值设为$x$, 转为二分第一个$>=0$的数)

在考虑递增区间, 假设当前一共有$r$个数

有转移方程 $f_i=min(a_i,f_{i-1}+1),$ $f$为得分, $a$为排序后数组

可以得到$f_r=min(a_r,a_{r-1}+1,a_{r-2}+2,...,a_{l}+r-l,x+(r-x))$, $a_{l}$为第一个比$x$大的数

用权值线段树维护最小值即可

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #define REP(i,a,n) for(int i=a;i<=n;++i)
 5 #define mid ((l+r)>>1)
 6 #define lc (o<<1)
 7 #define rc (lc|1)
 8 #define ls lc,l,mid
 9 #define rs rc,mid+1,r
10 using namespace std;
11
12 const int N = 5e5+10, INF = 0x3f3f3f3f;
13
14 int n, m, ans;
15 struct _ {
16     int mx,tag;
17     void upd(int x) {
18         mx+=x,tag+=x;
19     }
20 } v1[N<<2], v2[N<<3];
21
22 //v1维护的递减区间
23 //v2维护递增区间
24
25 void pd(_ *v, int o) {
26     if (v[o].tag) {
27         v[lc].upd(v[o].tag);
28         v[rc].upd(v[o].tag);
29         v[o].tag=0;
30     }
31 }
32
33 void add1(int o, int l, int r, int ql, int qr) {
34     if (ql<=l&&r<=qr) return v1[o].upd(1);
35     pd(v1,o);
36     if (mid>=ql) add1(ls,ql,qr);
37     if (mid<qr) add1(rs,ql,qr);
38     v1[o].mx=max(v1[lc].mx,v1[rc].mx);
39 }
40
41 void add2(int o, int l, int r, int ql, int qr) {
42     if (ql<=l&&r<=qr) return v2[o].upd(1);
43     pd(v2,o);
44     if (mid>=ql) add2(ls,ql,qr);
45     if (mid<qr) add2(rs,ql,qr);
46     v2[o].mx=min(v2[lc].mx,v2[rc].mx);
47 }
48
49 int qry1(int o, int l, int r) {
50     if (l==r) return l;
51     pd(v1,o);
52     if (v1[lc].mx>=0) return qry1(ls);
53     return qry1(rs);
54 }
55
56 void qry2(int o, int l, int r, int ql, int qr) {
57     if (v2[o].mx>=ans) return;
58     if (ql<=l&&r<=qr) return ans=min(ans,v2[o].mx),void();
59     pd(v2,o);
60     if (mid>=ql) qry2(ls,ql,qr);
61     if (mid<qr) qry2(rs,ql,qr);
62 }
63
64 void build1(int o, int l, int r) {
65     if (l==r) return v1[o].mx=l,void();
66     build1(ls),build1(rs);
67     v1[o].mx=max(v1[lc].mx,v1[rc].mx);
68 }
69
70 void build2(int o, int l, int r) {
71     if (l==r) return v2[o].mx=l,void();
72     build2(ls),build2(rs);
73     v2[o].mx=min(v2[lc].mx,v2[rc].mx);
74 }
75
76 int main() {
77     scanf("%d", &n);
78     build1(1,-N,0);
79     build2(1,-N,N);
80     REP(i,1,n) {
81         int k;
82         scanf("%d", &k);
83         if (k<0) add1(1,-N,0,k,N);
84         add2(1,-N,N,-N,k-1);
85         int x = qry1(1,-N,0);
86         ans = INF, qry2(1,-N,N,x,N);
87         printf("%d\n", ans);
88     }
89 }

原文地址:https://www.cnblogs.com/uid001/p/10248923.html

时间: 2024-07-29 05:17:19

Blog Post Rating CodeForces - 806E (线段树二分)的相关文章

codeforces 589G:线段树+二分

离线做 先按照t[]建线段树,维护区间的有效天数以及可用时间 然后把t[]从小到达排序,记得记录t中元素在线段树中的位置 把询问按照d从小到大排序,依次考虑 由于按照d排序,所以当前询问肯定是d最短的,我们在t数组中找到大于当前d的第一个元素位置,把这之前的元素全部从线段树中删除,表示这些天数无效 因为当前d已经是最小,所以删除对后续不会有影响 二分一个天数,查找0~mid天一共的工作时间(工作时间=总可用时间-准备时间*有效天数) #include"cstdio" #include&

Nastya and King-Shamans CodeForces - 992E (线段树二分)

大意: 给定序列a, 单点更新, 询问是否存在a[i]等于s[i-1], s为a的前缀和, a非负 考虑到前缀和的单调性, 枚举从1开始前缀和, 找到第一个大于等于s[1]的a[i], 如果相等直接输出. 若不满足则只能在a[i]的右侧, 此时前缀和为s[i], 至少为s[1]的两倍, 故最多进行log次. 具体实现的话, 线段树维护a与s的差, 二分找第一个非负即可, 复杂度$O(nlog^2n)$. #include <iostream> #include <algorithm>

Educational Codeforces Round 64 (Rated for Div. 2) (线段树二分)

题目:http://codeforces.com/contest/1156/problem/E 题意:给你1-n  n个数,然后求有多少个区间[l,r] 满足    a[l]+a[r]=max([l,r]) 思路:首先我们去枚举区间肯定不现实,我们只能取把能用的区间去用,我们可以想下每个数当最大值的时候所做的贡献 我们既然要保证这个数为区间里的最大值,我们就要从两边扩展,找到左右边界能扩展在哪里,这里你直接去枚举肯定不行 这里我们使用了线段树二分去枚举左右区间最远能走到哪里,然后很暴力的去枚举短

hdu 4893 (多校1007)Wow! Such Sequence!(线段树&amp;二分&amp;思维)

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 352    Accepted Submission(s): 104 Problem Description Recently, Doge got a funny birthday present from his new friend, Prot

Codeforces 384E 线段树+dfs序

题目链接:点击打开链接 题意: 给定n个点,m个询问的无向树(1为根) 下面n个数表示每个点的权值 下面n-1行给出树 操作1:x点权值+v, x的第 i & 1 的儿子-v, 第 !(i&1) 的儿子+v 操作2:询问x点权值 dfs把树转成序列 根据深度把点分成2组 分别用线段树维护.. 然后Y一下 #include<stdio.h> #include<string.h> #include<iostream> #include<algorith

hdu 5592 ZYB&#39;s Premutation (线段树+二分查找)

链接: http://acm.hdu.edu.cn/showproblem.php?pid=5592 Problem Description ZYB has a premutation P,but he only remeber the reverse log of each prefix of the premutation,now he ask you to  restore the premutation.Pair (i,j)(i<j) is considered as a reverse

Codeforces 446C 线段树 递推Fibonacci公式

聪哥推荐的题目 区间修改和区间查询,但是此题新颖之处就在于他的区间修改不是个定值,而是从L 到 R 分别加 F1.F2....Fr-l+1 (F为斐波那契数列) 想了一下之后,觉得用fib的前缀和来解决,每次做懒惰标记记录下当前区间是从哪个L开始加起的,敲了一半之后发现有问题,就跟上次遇到的懒惰标记问题一样,这是个覆盖性的懒惰标记,每次向下传递后,都要先清除孩子的,清除孩子的也有可能要清除son's son,所以要一直pushdown下去,否则就会错,但这样就会超时. 能不能有个累加型的标记让我

Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论

Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论 题意 给你一段数,然后小明去猜某一区间内的gcd,这里不一定是准确值,如果在这个区间内改变一个数的值(注意不是真的改变),使得这个区间的gcd是小明所猜的数也算小明猜对.另一种操作就是真的修改某一点的值. 解题思路 这里我们使用线段树,维护区间内的gcd,判断的时候需要判断这个区间的左右子区间的gcd是不是小明猜的数的倍数或者就是小明猜的数,如果是,那么小明猜对了.否则就需要进入这个区间

codeforces 487B B. Strip(rmq+线段树+二分)

题目链接: codeforces 487B 题目大意: 给出一个序列,要把序列划分成段,每一段最少有L个元素,段中的最大元素和最小元素之差不大于s,问划分的段的最少的数量是多少. 题目分析: 首先用rmq维护区间最大值和区间最小值. 然后按顺序扫描数组,线段树维护的数组,每个记录当前点作为最后一个点的前i个点划分的最小的段数,那么每次更新就是二分找到可以转移到我的最远距离,然后再选取与我距离大于l的那部分,取最小值即可. 最终结果就是线段树维护的数组的最后一个位置的元素的值. AC代码: #in