Philosopher(set 线段树合并)

Posted luoyibujue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Philosopher(set 线段树合并)相关的知识,希望对你有一定的参考价值。

  • 直接维护乘积是肯定不可行的, 精度会爆炸, 于是我们来维护对数的和, 最后来计算最高位即可
  • 那么转换成区间求和, 区间排序
  • 区间排序的方式可以采用线段树维护最大递增块来解决,外层用set来维护线段树的区间, 然后利用线段树的合并分裂性质来操作即可
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<iostream>
#include<set>
#include<cmath>
#define ll long long
#define M (1 << 18)
#define N 20000010
#define double long double
const double eps = 1e-8;
using namespace std;
int read() 
    int nm = 0, f = 1;
    char c = getchar();
    for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
    return nm * f;

double c[M], ver[M];
int num[M], n, m;
int lowbit(int x) 
    return x & -x;

void add(int x, double v) 
    for(int i = x; i <= n; i += lowbit(i)) c[i] += v;


double query(int x) 
    double ans = 0;
    for(int i = x; i; i -= lowbit(i)) ans += c[i];
    return ans;


struct Note 
    int l, r, rt, op;
    Note(int ln = 0, int rn = 0, int rtn = 0, int opn = 0) 
        l = ln, r = rn, rt = rtn, op = opn;
    
    bool operator < (const Note &b) const 
        return this->l < b.l;
    
;
#define S set<Note>::iterator
set<Note> st;
int ls[N], rs[N], cnt[N], f;
double sum[N];

void pushup(int now) 
    cnt[now] = cnt[ls[now]] + cnt[rs[now]];
    sum[now] = sum[ls[now]] + sum[rs[now]];


int merge(int x, int y) 
    if(!x || !y) return x + y;
    ls[x] = merge(ls[x], ls[y]);
    rs[x] = merge(rs[x], rs[y]);
    cnt[x] = cnt[x] + cnt[y];
    sum[x] = sum[x] + sum[y];
    return x;


S insert(Note x) 
    add(x.l, sum[x.rt]);
    return st.insert(x).first;


void Del(S it) 
    add(it->l, -sum[it->rt]);
    st.erase(it);


void split(int x, int &rt1, int &rt2, int l, int r, int k) 
    rt1 = ++f;
    rt2 = ++f;
    if(l == r) 
        cnt[rt1] = k;
        sum[rt1] = ver[l] * k;
        cnt[rt2] = cnt[x] - cnt[rt1];
        sum[rt2] = sum[x] - sum[rt1];
        return;
    
    int mid = (l + r) >> 1;
    if(cnt[ls[x]] >= k) 
        rs[rt2] = rs[x];
        split(ls[x], ls[rt1], ls[rt2], l, mid, k);
     else 
        ls[rt1] = ls[x];
        split(rs[x], rs[rt1], rs[rt2], mid + 1, r, k - cnt[ls[x]]);
    
    pushup(rt1);
    pushup(rt2);


S split(int x) 
    if(x > n) return st.end();
    S it = st.upper_bound(Note(x, 0, 0, 0));
    it--;
    Note hh = *it;
    if(hh.l == x) return it;
    int rt1, rt2;
    if(!hh.op) split(hh.rt, rt1, rt2, 1, n, x - hh.l);
    else split(hh.rt, rt2, rt1, 1, n, hh.r - x + 1);
    Del(it);
    insert(Note(hh.l, x - 1, rt1, hh.op));
    return insert(Note(x, hh.r, rt2, hh.op));


void build(int &x, int l, int r, int k) 
    x = ++f;
    cnt[x]++;
    sum[x] += ver[k];
    if(l == r) return;
    int mid = (l + r) >> 1;
    if(k <= mid) build(ls[x], l, mid, k);
    else build(rs[x], mid + 1, r, k);



int calc(int l, int r) 
    S L = split(l), R = split(r + 1);
    R--;
    double ans = query(R->r) - query(L->l - 1);
    double out = pow(10, ans - floorl(ans) + eps);
    return floorl(out);


void updata(int l, int r, int op) 
    S L = split(l);
    split(r + 1);
    int rt = 0;
    for(S it = L; it != st.end() && (it->l) <= r; Del(it++)) 
        rt = merge(rt, it->rt);
    
    insert(Note(l, r, rt, op));


int main() 
    n = read(), m = read();
    for(int i = 1; i <= n; i++) num[i] = read(), ver[i] = log10(i);
    for(int i = 1; i <= n; i++) 
        int now;
        build(now, 1, n, num[i]);
        insert(Note(i, i, now, 0));
    
    while(m--) 
        int op = read(), l = read(), r = read();
        if(op == 2) cout << calc(l, r) << "\n";
        else 
            op = read() ^ 1;
            updata(l, r, op);
        
    
    return 0;

以上是关于Philosopher(set 线段树合并)的主要内容,如果未能解决你的问题,请参考以下文章

HDU 3911 线段树区间合并

BZOJ4561:圆的异或并(扫描线+set||splay||线段树)

HDU 1540(线段树+区间合并)学习记录

线段树合并

线段树合并

BZOJ 4756 线段树合并(线段树)