【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 }