CodeForces992E 二分 + 树状数组(线段树)

http://codeforces.com/problemset/problem/992/E

题意:给定一个序列 ai? ,记其前缀和序列为 si? ,有 q 个询问,每次单点修改,询问是否存在一个 i 满足 ai?=si−1?,有多解输出任意一个,无解输出 -1。

思路一:构造一个b[i] = a[i] - s[i - 1]的序列,答案就是在其中寻找为0的位置,对每一次操作进行一个线段树的单点修改和区间修改之后对一整个区间寻找是否存在0的位置,但是最坏的情况下能达到N * Q * lnN,虽然据说可过但是觉得并不靠谱

思路2:ai = si - 1,考虑转化一下就变成了si = 2 * s(i - 1),所以对于起始位置x,下一个可能符合答案ans - 1的位置的就是最大的sk < 2 * sx,因为x到k中间的位置很显然前一个数的s会比sx大,后一个数的位置会比sk小,很显然并不满足。

所以可以考虑用树状数组维护前缀和之后用一个类似跳的算法加一个二分寻找ans,时间复杂度是QlnNlnNlnN,有点慢但依然可以过。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
inline int read(){int now=0;register char c=getchar();for(;!isdigit(c);c=getchar());
for(;isdigit(c);now=now*10+c-‘0‘,c=getchar());return now;}
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-9;
const int maxn = 2e5 + 10;
const int maxm = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int N,Q;
LL a[maxn];
LL tree[maxn];
void add(int x,int t){for(;x <= N ; x += x & -x) tree[x] += t;}
LL getsum(int x){LL ans = 0; for(;x > 0 ; x -= x & -x) ans += tree[x]; return ans;}
void solve(){
    if(a[1] == 0) return (void)puts("1");
    int x = 1;
    while(x < N){
        LL sum = getsum(x) * 2;
        if(getsum(x + 1) == sum) return (void)printf("%d\n",x + 1);
        int ans = x,l = x + 1,r = N;
        while(l <= r){
            int m = (l + r) >> 1;
            if(getsum(m) < sum){
                ans = m;
                l = m + 1;
            }else{
                r = m - 1;
            }
        }
        if(ans > N) break;
        x = (x == ans)?ans + 1:ans;
    }
    puts("-1");
}
int main()
{
    Sca2(N,Q);
    for(int i = 1; i <= N ; i ++){
        Scl(a[i]); add(i,a[i]);
    }
    while(Q--){
        int x; LL p;
        scanf("%d%lld",&x,&p);
        add(x,p - a[x]); a[x] = p;
        solve();
    }

    return 0;
}

思路2

思路3:将跳的思路改为寻找一个最小的大于si的位置,考虑用线段树维护一下区间最大值,树状数组维护前缀和,可以优化掉一个ln

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
inline int read(){int now=0;register char c=getchar();for(;!isdigit(c);c=getchar());
for(;isdigit(c);now=now*10+c-‘0‘,c=getchar());return now;}
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-9;
const int maxn = 2e5 + 10;
const int maxm = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int N,Q;
LL fa[maxn],a[maxn];
void add(int p,int t){
    for(;p <= N ; p += p & -p) fa[p] += t;
}
LL getsum(int p){
    LL ans = 0;
    for(;p > 0; p -= p & -p) ans += fa[p];
    return ans;
}
struct Node{
    int pos;
    LL MAX;
    Node(int pos = 0,LL MAX = 0):pos(pos),MAX(MAX) {}
};
Node operator + (Node a,Node b){
    if(a.MAX >= b.MAX) return a;
    return b;
}
Node operator - (Node a,Node b){
    if(a.pos >= b.pos) return b;
    return a;
}
struct Tree{
    int l,r;
    Node MAX;
}tree[maxn << 2];
void Build(int t,int l,int r){
    tree[t].l = l; tree[t].r = r;
    if(l == r){
        tree[t].MAX.MAX = a[l];
        tree[t].MAX.pos = l;
        return;
    }
    int m = (l + r) >> 1;
    Build(t << 1,l,m); Build(t << 1 | 1,m + 1,r);
    tree[t].MAX = tree[t << 1].MAX + tree[t << 1 | 1].MAX;
}
void update(int t,int pos,LL val){
    if(tree[t].l == tree[t].r){
        tree[t].MAX.MAX = val;
        return;
    }
    int m = (tree[t].l + tree[t].r) >> 1;
    if(pos <= m) update(t << 1,pos,val);
    else update(t << 1 | 1,pos,val);
    tree[t].MAX = tree[t << 1].MAX + tree[t << 1 | 1].MAX;
}
Node query(int t,int l,int r,LL sum){
    if(l <= tree[t].l && tree[t].r <= r){
        if(tree[t].MAX.MAX < sum) return Node(INF,1e18);
        if(tree[t].l == tree[t].r) return tree[t].MAX;
        int m = (tree[t].l + tree[t].r) >> 1;
        if(tree[t << 1].MAX.MAX >= sum) return query(t << 1,l,m,sum);
        return query(t << 1 | 1,m + 1,r,sum);
    }
    int m = (tree[t].l + tree[t].r) >> 1;
    if(r <= m) return query(t << 1,l,r,sum);
    else if(l > m) return query(t << 1 | 1,l,r,sum);
    else return query(t << 1,l,m,sum) - query(t << 1 | 1,m + 1,r,sum);
}

int solve(){
    if(getsum(1) == 0) return 1;
    int s = 1;
    while(s < N){
        LL now = getsum(s);
        Node MAX = query(1,s + 1,N,now);
        if(MAX.pos == INF) return -1;
        if(getsum(MAX.pos - 1) == MAX.MAX) return MAX.pos;
        s = MAX.pos;
    }
    return -1;
}
int main()
{
    Sca2(N,Q);
    for(int i = 1; i <= N ; i ++){
        Scl(a[i]);
        add(i,a[i]);
    }
    Build(1,1,N);
    while(Q--){
        int x; LL p;
        scanf("%d%lld",&x,&p);
        update(1,x,p);
        add(x,p - a[x]); a[x] = p;
        Pri(solve());
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Hugh-Locke/p/9977966.html

时间: 2024-11-12 06:30:13

CodeForces992E 二分 + 树状数组(线段树)的相关文章

hdu 1166 树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 51177    Accepted Submission(s): 21427 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务

hdu1394(枚举/树状数组/线段树单点更新&amp;区间求和)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给出一个循环数组,求其逆序对最少为多少: 思路:对于逆序对: 交换两个相邻数,逆序数 +1 或 -1, 交换两个不相邻数 a, b, 逆序数 += 两者间大于 a 的个数 - 两者间小于 a 的个数: 所以只要求出初始时的逆序对数,就可以推出其余情况时的逆序对数.对于求初始逆序对数,这里 n 只有 5e3,可以直接暴力 / 树状数组 / 线段树 / 归并排序: 代码: 1.直接暴力 1

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 1166 敌兵布阵 (树状数组&#183;线段树)

题意  中文 动态区间和问题   只会更新点  最基础的树状数组 线段树的应用 树状数组代码 #include <bits/stdc++.h> using namespace std; const int N = 50005; int c[N], n, m; void add(int p, int x) { while(p <= n) c[p] += x, p += p & -p; } int getSum(int p) { int ret = 0; while(p > 0

二分+树状数组/线段树(区间更新) HDOJ 4339 Query

题目传送门 题意:给两串字符串,操作1:替换其中一个字符串的某个位置的字符 操作2:查询从p开始相等的最长连续长度 分析:树状数组可以维护一个区间内公共长度(连续)的情况,查询时用二分查找最远的端点即可.还可以用线段树去做,线段树能处理的问题很多,这题只要往右区间合并就行了. 收获:1.线段树的区间合并又做一题(虽然我写的还没AC) 2. 树状数组写起来方便又奇效! 代码1(树状数组): /************************************************ * Au

Curious Robin Hood(树状数组+线段树)

1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 MB Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another tri

士兵杀敌(四)(树状数组+线段树)

士兵杀敌(四) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情,军师小工的任务就是在南将军询问他某个人的军功的时候,快速的报出此人的军功,请你编写一个程序来帮助小工吧. 假设起始时所有人的军功都是0. 输入

Color the ball(树状数组+线段树)

Color the ball Time Limit : 9000/3000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 3   Accepted Submission(s) : 1 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b

Codeforces Round #225 (Div. 1) C 树状数组 || 线段树

看到这题很开心啊,有印象跟以前做过的很像,貌似最近就做过一个,以时间戳为区间来建立树状数组,然后一开始我以为题意是,给x点加val,它以下的所有节点都加-val:所以一开始就以 加 和 减 建立了两个树状数组,最后 减去就是答案,写完发现跟案例对不上啊,读了题目也没发现读错了,对于那句话 我理解错了,后来看了 这个: http://blog.csdn.net/keshuai19940722/article/details/18967661 仔细看看处理部分,我还以为分奇偶性有规律呢,后来才发现读

HDU 4417 Super Mario (树状数组/线段树)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble agai