Philosopher
给你一个长度为\(n\)的序列\(a_i\)和\(m\)次操作,要支持两种操作:
\(1\ l\ r\ flag\):将\([l,r]\)内的数排序,\(flag=1\)表示排成升序,否则是降序
\(2\ l\ r\):询问\([l,r]\)内的数的乘积的最高位是多少
数据范围:\(n,m<=2*10^5\),\(1\leq a_i\leq n\)
Solution
首先考虑怎么处理询问:一个很棒的idea是转成维护\(lg\),取对数之后乘法就被转成了加法,我们只要维护区间\(lg\)的和\(sum\),还原的话整数部分不需要管,所以只要取\(pow(10,sum-\lfloor sum \rfloor)\)的首位即可
那么剩下的就是如何处理区间排序操作了
显然的一点是:被\(op1\)操作过的区间一定是有序的,那么一个大胆的想法是把操作过而变得有序的区间内的数放在一起维护,具体来说就是对于每一个操作过的区间种一棵权值线段树(当然要动态开点),这样我们就可以快速查询每一块中的前缀和或者后缀和,升序降序直接整块记录一下查询的时候判断一下就好了
然后我们再用一棵大线段树来维护整个序列,具体来说就是将每一块(也就是每一颗权值线段树)的根节点的信息(\(lg\)和)存到这一块最左边的那个位置上,其他位置全部为\(0\)
这样一来我们只要实现一个线段树的分离操作和合并操作、以及快速确定一个位置\(p\)属于哪一个块(这个可以通过大线段树维护一个区间块头的\(min\)来实现)就可以了
修改的时候就一头一尾判一下是否需要split,然后中间一段一直合并;询问就一直跳\(l\)的块就好了,主要考虑\(l\)不是块头的情况
时间复杂度的话。。由于修改每次最多增加一个块,所以总复杂度是均摊的,具体的话势能分析一下(然而因为我比较菜所以只能口胡==),每次操作是均摊\(log\)的,总的就是常数巨大的\(O(nlogn)\)
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ldb long double
#define Pr pair<int,int>
#define mp make_pair
using namespace std;
const int N=2e5+10;
int a[N],dir[N];//1==up 0==down
ldb lg[N];
int n,m,ans;
namespace Seg{/*{{{*/
const int N=::N*40;
int ch[N][2],rt[::N],cnt[N];
int pool[N];
ldb sum[N];
int n,tot,tp;
void pushup(int x){
cnt[x]=cnt[ch[x][0]]+cnt[ch[x][1]];
sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
}
void init(int _n){n=_n; tot=tp=0;}
void del(int x){pool[++tp]=x;}
int newnode(){
tot=tp?pool[tp--]:++tot;
cnt[tot]=sum[tot]=0; ch[tot][0]=ch[tot][1]=0;
return tot;
}
void _insert(int &x,int d,int lx,int rx,ldb delta){
if (!x) x=newnode();
++cnt[x];
sum[x]+=delta;
if (lx==rx) return;
int mid=lx+rx>>1;
if (d<=mid) _insert(ch[x][0],d,lx,mid,delta);
else _insert(ch[x][1],d,mid+1,rx,delta);
}
void insert(int x,int d){_insert(rt[x],d,1,n,lg[d]);}
void merge(int &x,int y){
if (!x||!y){x=x+y; return;}
merge(ch[x][0],ch[y][0]);
merge(ch[x][1],ch[y][1]);
sum[x]+=sum[y];
cnt[x]+=cnt[y];
del(y);
}
Pr _split(int x,int d,int lx,int rx){
if (!x||!d) return mp(0,x);
if (cnt[x]==d) return mp(x,0);
int nw=newnode();
if (lx==rx){
sum[nw]=lg[lx]*d;
cnt[nw]=d;
sum[x]-=sum[nw];
cnt[x]-=cnt[nw];
return mp(nw,x);
}
int lcnt=cnt[ch[x][0]],mid=lx+rx>>1;
Pr tmp;
if (d<=lcnt){
tmp=_split(ch[x][0],d,lx,mid);
ch[x][0]=tmp.second;
ch[nw][0]=tmp.first;
pushup(x);
pushup(nw);
return mp(nw,x);
}
else{
tmp=_split(ch[x][1],d-lcnt,mid+1,rx);
ch[x][1]=tmp.first;
ch[nw][1]=tmp.second;
pushup(x);
pushup(nw);
return mp(x,nw);
}
}
Pr split(int x,int d){
Pr ret;
if (dir[x])
ret=_split(rt[x],d,1,n);
else{
ret=_split(rt[x],cnt[rt[x]]-d,1,n);
swap(ret.first,ret.second);
}
return ret;
}
ldb _query(int x,int k,int lx,int rx){
if (!x||!k) return 0;
if (lx==rx) return k*lg[lx];
int lcnt=cnt[ch[x][0]],mid=lx+rx>>1;
if (k>=lcnt) return sum[ch[x][0]]+_query(ch[x][1],k-lcnt,mid+1,rx);
return _query(ch[x][0],k,lx,mid);
}
ldb query(int x,int k){
if (dir[x])
return _query(rt[x],k,1,n);
else
return sum[rt[x]]-_query(rt[x],cnt[rt[x]]-k,1,n);
}
}/*}}}*/
namespace Seg2{/*{{{*/
const int N=::N*4;
int ch[N][2],mn[N];
ldb sum[N];
int n,tot;
void pushup(int x){
sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
mn[x]=min(mn[ch[x][0]],mn[ch[x][1]]);
}
void _build(int x,int l,int r){
if (l==r){
sum[x]=Seg::sum[Seg::rt[l]];
mn[x]=l;
return;
}
int mid=l+r>>1;
ch[x][0]=++tot; _build(ch[x][0],l,mid);
ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
pushup(x);
}
void build(int _n){n=_n; tot=1; _build(1,1,n);}
void _update(int x,int d,int lx,int rx){
if (lx==rx){
mn[x]=Seg::rt[lx]?lx:n+1;
sum[x]=Seg::sum[Seg::rt[lx]];
return;
}
int mid=lx+rx>>1;
if (d<=mid) _update(ch[x][0],d,lx,mid);
else _update(ch[x][1],d,mid+1,rx);
pushup(x);
}
void update(int d){_update(1,d,1,n);}
int _get_p(int x,int d,int lx,int rx){
if (mn[x]>d) return -1;
if (lx==rx) return lx;
int mid=lx+rx>>1,ret=-1;
if (d>mid) ret=_get_p(ch[x][1],d,mid+1,rx);
if (d<=mid||ret==-1) ret=_get_p(ch[x][0],d,lx,mid);
return ret;
}
int get_p(int d){return _get_p(1,d,1,n);}//the hd which d belongs to
ldb _query(int x,int l,int r,int lx,int rx){
if (l<=lx&&rx<=r) return sum[x];
int mid=lx+rx>>1;
if (r<=mid) return _query(ch[x][0],l,r,lx,mid);
else if (l>mid) return _query(ch[x][1],l,r,mid+1,rx);
else return _query(ch[x][0],l,mid,lx,mid)+_query(ch[x][1],mid+1,r,mid+1,rx);
}
ldb query(int l,int r){return _query(1,l,r,1,n);}
}/*}}}*/
ldb get_val(int x,int l,int r){
ldb tmp1=l-1?Seg::query(x,l-1):0;
ldb tmp2=Seg::query(x,r);
return tmp2-tmp1;
}
void prework(int n){
for (int i=1;i<=n;++i) lg[i]=log10(i);
//for (int i=1;i<=n;++i) lg[i]=i;
Seg::init(n);
for (int i=1;i<=n;++i)
Seg::insert(i,a[i]);
Seg2::build(n);
}
void modify(int l,int r,int op){
using Seg::rt;
int tmp,hd,sz;
Pr pr;
hd=Seg2::get_p(r);
sz=Seg::cnt[rt[hd]];
if (hd+sz-1>r){//split r
pr=Seg::split(hd,r-hd+1);
rt[hd]=pr.first; rt[r+1]=pr.second;
dir[r+1]=dir[hd];
Seg2::update(r+1);
}
tmp=0;
while (hd>l){//merge mid
Seg::merge(tmp,rt[hd]);
rt[hd]=0;
Seg2::update(hd);
hd=Seg2::get_p(r);
}
if (hd<l){//split l
pr=Seg::split(hd,l-hd);
rt[hd]=pr.first; rt[l]=pr.second;
Seg2::update(hd);
}
Seg::merge(rt[l],tmp);
dir[l]=op;
Seg2::update(l);
}
int query(int l,int r){
using Seg::rt;
int hd=Seg2::get_p(r);
ldb ret;
if (hd<=l){//same
ret=get_val(hd,l-hd+1,r-hd+1);
}
else{
ret=Seg2::query(l,hd-1);
ret+=get_val(hd,1,r-hd+1);
hd=Seg2::get_p(l);
if (hd<l)//l --> not hd
ret+=get_val(hd,l-hd+1,Seg::cnt[rt[hd]]);
}
ret=pow(10,ret-floor(ret));
return (int)ret;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int op,l,r,dir;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i) scanf("%d",a+i);
prework(n);
for (int i=1;i<=m;++i){
scanf("%d%d%d",&op,&l,&r);
if (op==1){
scanf("%d",&dir);
modify(l,r,dir);
}
else{
ans=query(l,r);
printf("%d\n",ans);
}
}
}