1. 程式人生 > >CodeForces992E 二分 + 樹狀陣列(線段樹)

CodeForces992E 二分 + 樹狀陣列(線段樹)

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

題意:給定一個序列 ai ,記其字首和序列為 si ,有 q 個詢問,每次單點修改,詢問是否存在一個 i 滿足 ai=si1,有多解輸出任意一個,無解輸出 -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;
}