### Description

​  给你一个长度为\(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);
}
}
}

Philosopher的更多相关文章

  1. Philosopher’s Walk(递归)

    In Programming Land, there are several pathways called Philosopher’s Walks for philosophers to have ...

  2. Philosopher’s Walk --DFS

    题意: Philosopher’s Walk 图,告诉你step返回位置. 思路: 按四个块DFS #define IOS ios_base::sync_with_stdio(0); cin.tie( ...

  3. 【xsy2194】Philosopher set+线段树合并

    题目大意:给你一个长度为$n$的序列,有$m$次操作,每次操作是以下两种之一: 对某个区间内的数按照升序/降序排序,询问某个区间内数的积在十进制下首位数字是多少. 数据范围:$n,m≤2\times ...

  4. Philosopher(set 线段树合并)

    直接维护乘积是肯定不可行的, 精度会爆炸, 于是我们来维护对数的和, 最后来计算最高位即可 那么转换成区间求和, 区间排序 区间排序的方式可以采用线段树维护最大递增块来解决,外层用set来维护线段树的 ...

  5. 【英语学习】2016.09.11 Culture Insider: Teacher's Day in ancient China

      Culture Insider: Teacher's Day in ancient China 2016-09-10 CHINADAILY Today is the 32nd Chinese Te ...

  6. 第一篇英文短文《It All Starts With A Dream》

    http://www.ximalaya.com/#/17209107/sound/6883165 Dreaming. Do you or don’t you? Do you dream about t ...

  7. cs107

    基本类型:bool,char,short,int,long,float,double 对于char,short,int,long: 多字节类型赋值给少字节类型,对低字节的细节感兴趣,位模式拷贝. 少字 ...

  8. Python 3.X简史——记录3.0之后的重要更新

    Python 3.0在2008年12月3日正式发布,在之后又经历了多个小版本(3.1,3.2,3.3……),本文梳理Python 3.0之后的新特性. 其实每个版本都有大量更新,都写出来要几百页,这里 ...

  9. 【英语魔法俱乐部——读书笔记】 3 高级句型-简化从句&倒装句(Reduced Clauses、Inverted Sentences) 【完结】

    [英语魔法俱乐部——读书笔记] 3 高级句型-简化从句&倒装句(Reduced Clauses.Inverted Sentences):(3.1)从属从句简化的通则.(3.2)形容词从句简化. ...

随机推荐

  1. Java 多线程(四)之守护线程(Daemon)

    目录 定义 如何创建 判断 注意事项 函数setDaemon(true)必须在 start() 函数之前使用. 守护线程中产生的线程也是守护线程: 测试 @ 定义 Java 中有两种线程: 一种是用户 ...

  2. 【ORACLE】重写控制文件

    [oracle@rac01 ~]$ sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production on Thu Mar 15 23:45:02 ...

  3. 《无所不能的JavaScript编程系列:setTimeout 简笔》

    前言:问题引出 JavaScript中会经常用到setTimeout来推迟一个函数的执行,如: setTimeout(function(){alert("Hello World") ...

  4. tomcat-内存溢出java.lang.OutOfMemoryErrory:PermGen space解决方法

    如果是PermGen space方法区内存溢出,可尝试加大MaxPermSize,如果是heap space 堆内存移除,可尝试修改Xmx 正常解决方法: 在注释下的第一行添加: JAVA_OPTS= ...

  5. 1093. Count PAT’s (25)-统计字符串中PAT出现的个数

    如题,统计PAT出现的个数,注意PAT不一定要相邻,看题目给的例子就知道了. num1代表目前为止P出现的个数,num12代表目前为止PA出现的个数,num123代表目前为止PAT出现的个数. 遇到P ...

  6. 【Alpha】第五次Scrum meeting

    今日重大事件一览: 姓名 今日完成任务 所耗时间 刘乾 今日没有完成那个Issue..TuT第一次这么努力工作的我没有完成任务...真的是任务太坑啦. 任务完成了 60% Issue链接:https: ...

  7. java常见编码

    摘自:http://www.cnblogs.com/yaya-yaya/p/5768616.html 红色 主要点    灰色 内容      绿色  知识点    橘色 补充内容 几种常见的编码格式 ...

  8. django学习--2 模板

    Django 模板 在上一章节中我们使用 django.http.HttpResponse() 来输出 "Hello World!".该方式将数据与视图混合在一起,不符合 Djan ...

  9. .NET获取文件的MIME类型(Content Type)

    第一种:这种获取MIME类型(Content Type)的方法需要在.NET 4.5之后才能够支持,但是非常简单. 优点:方便快捷 缺点:只能在.NET 4.5之后使用 public FileResu ...

  10. C#解析数组形式的json数据

    在学习时遇到把解析json数据的问题,网上也搜了很多资料才得以实现,记录下来以便翻阅. 1. 下载开源的类库Newtonsoft.Json(下载地址http://json.codeplex.com/, ...