【xsy2194】Philosopher set+线段树合并

题目大意:给你一个长度为$n$的序列,有$m$次操作,每次操作是以下两种之一:

对某个区间内的数按照升序/降序排序,询问某个区间内数的积在十进制下首位数字是多少。

数据范围:$n,m≤2\times 10^5$ 序列内数字均不大于$n$。

 

我们先考虑下如何实现查询首位数字

我们发现如果直接乘的话精度损失实在太大,我们考虑把所有读入的数字全部转成对数,直接加起来。

设某个区间内对数和为$x$,那么该区间内数的积的首位为$\lfloor 10^{x-\lfloor x\rfloor} \rfloor$。

 

下面考虑如何做排序操作

我们先种一堆的线段树,满足每一棵线段树维护的是一个已经按照升序/降序排序好的区间(内层线段树存储该区间内放了值为哪些的数)

我们对某个区间进行排序的时候,我们要取出若干个线段树,满足这些线段树构成的区间恰好为需要排序的区间。

然后我们通过线段树合并将这些区间合并为一个区间即可,最后打上这个区间的升序/降序标记

 

然而有些线段树被我们需要操作的区间部分包含。

这种情况下就需要把这棵线段树从中间某个位置开始拆开。

考虑到一棵线段树所代表的区间内的数都是排序好的,我们可以根据升序/降序标记来拆开这棵线段树。

每一棵线段树的根我们可以开一个$set$来存储,每一棵线段树内的对数和我们可以单独打在一个树状数组上。

 

在查询答案的时候,我们按照上面做排序的方法,拆出若干棵线段树,然后直接在线段树上查询即可。

时间复杂度:$O(n\log^2\ n)$

 

  1 #include<bits/stdc++.h>
  2 #define D long double
  3 #define M (1<<18)
  4 #define lowbit(x) (x&(-x))
  5 #define N 20000005
  6 #define S set<node>::iterator
  7 #define eps (1e-8)
  8 using namespace std;
  9 
 10 int n,m; D c[M]={0}; void add(int x,D k){for(int i=x;i<=n;i+=lowbit(i)) c[i]+=k;}
 11 D query(int x){D k=0; for(int i=x;i;i-=lowbit(i)) k+=c[i]; return k;}
 12 int num[M]={0}; D val[M]={0};
 13 
 14 struct node{
 15     int l,r,rt,op;
 16     node(int ll=0,int rr=0,int RT=0,int OP=0){l=ll; r=rr; rt=RT; op=OP;}
 17     friend bool operator <(node a,node b){return a.l<b.l;}
 18 };set<node> s;
 19 
 20 int lc[N]={0},rc[N]={0},cnt[N]={0},use=0; D sum[N]={0};
 21 void pushup(int x){
 22     cnt[x]=cnt[lc[x]]+cnt[rc[x]];
 23     sum[x]=sum[lc[x]]+sum[rc[x]];
 24 }
 25 int merge(int x,int y){
 26     if(!x||!y) return x|y;
 27     lc[x]=merge(lc[x],lc[y]);
 28     rc[x]=merge(rc[x],rc[y]);
 29     cnt[x]=cnt[x]+cnt[y];
 30     sum[x]=sum[x]+sum[y];
 31     return x;
 32 }
 33 
 34 S Ins(node x){add(x.l,sum[x.rt]);return s.insert(x).first;}
 35 void Del(S it){add(it->l,-sum[it->rt]);s.erase(it);}
 36 
 37 void split(int x,int &rt1,int &rt2,int l,int r,int k){
 38     rt1=++use; rt2=++use;
 39     if(l==r){
 40         cnt[rt1]=k; sum[rt1]=val[l]*k;
 41         cnt[rt2]=cnt[x]-cnt[rt1];
 42         sum[rt2]=sum[x]-sum[rt1];
 43         return;
 44     }
 45     int mid=(l+r)>>1;
 46     if(cnt[lc[x]]>=k){
 47         rc[rt2]=rc[x];
 48         split(lc[x],lc[rt1],lc[rt2],l,mid,k);
 49     }else{
 50         lc[rt1]=lc[x];
 51         split(rc[x],rc[rt1],rc[rt2],mid+1,r,k-cnt[lc[x]]);
 52     }
 53     pushup(rt1); pushup(rt2);
 54 }
 55 S split(int x){
 56     if(x>n) return s.end();
 57     S it=s.upper_bound(node(x,0,0,0)); it--;
 58     node hh=*it; if(hh.l==x) return it;
 59     int rt1,rt2;
 60     if(!hh.op) split(hh.rt,rt1,rt2,1,n,x-hh.l);
 61     else split(hh.rt,rt2,rt1,1,n,hh.r-x+1);
 62     Del(it);
 63     Ins(node(hh.l,x-1,rt1,hh.op));
 64     return Ins(node(x,hh.r,rt2,hh.op));
 65 }
 66 
 67 void updata(int l,int r,int op){
 68     S L=split(l); split(r+1); int rt=0;
 69     for(S it=L;it!=s.end()&&(it->l)<=r;Del(it++)){
 70         rt=merge(rt,it->rt);
 71     }
 72     Ins(node(l,r,rt,op));
 73 }
 74 int calc(int l,int r){
 75     S L=split(l),R=split(r+1); R--;
 76     D ans=query(R->r)-query(L->l-1);
 77     D out=pow(10,ans-floorl(ans)+eps);
 78     return floorl(out);
 79 }
 80 
 81 void build(int &x,int l,int r,int k){
 82     x=++use; cnt[x]++; sum[x]+=val[k];
 83     if(l==r) return; int mid=(l+r)>>1;
 84     if(k<=mid) build(lc[x],l,mid,k); 
 85     else build(rc[x],mid+1,r,k);
 86 }
 87 
 88 int main(){
 89     scanf("%d%d",&n,&m);
 90     for(int i=1;i<=n;i++) scanf("%d",num+i);
 91     for(int i=1;i<=n;i++) val[i]=log10(i);
 92     for(int i=1;i<=n;i++){
 93         int now; build(now,1,n,num[i]);
 94         Ins(node(i,i,now,0));
 95     }
 96     while(m--){
 97         int op,l,r; scanf("%d%d%d",&op,&l,&r);
 98         if(op==2) printf("%d\n",calc(l,r));
 99         else{
100             scanf("%d",&op); op^=1;
101             updata(l,r,op);
102         }
103     }
104 }
posted @ 2019-03-23 08:48  AlphaInf  阅读(194)  评论(0编辑  收藏  举报