Philosopher
### 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的更多相关文章
- Philosopher’s Walk(递归)
In Programming Land, there are several pathways called Philosopher’s Walks for philosophers to have ...
- Philosopher’s Walk --DFS
题意: Philosopher’s Walk 图,告诉你step返回位置. 思路: 按四个块DFS #define IOS ios_base::sync_with_stdio(0); cin.tie( ...
- 【xsy2194】Philosopher set+线段树合并
题目大意:给你一个长度为$n$的序列,有$m$次操作,每次操作是以下两种之一: 对某个区间内的数按照升序/降序排序,询问某个区间内数的积在十进制下首位数字是多少. 数据范围:$n,m≤2\times ...
- Philosopher(set 线段树合并)
直接维护乘积是肯定不可行的, 精度会爆炸, 于是我们来维护对数的和, 最后来计算最高位即可 那么转换成区间求和, 区间排序 区间排序的方式可以采用线段树维护最大递增块来解决,外层用set来维护线段树的 ...
- 【英语学习】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 ...
- 第一篇英文短文《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 ...
- cs107
基本类型:bool,char,short,int,long,float,double 对于char,short,int,long: 多字节类型赋值给少字节类型,对低字节的细节感兴趣,位模式拷贝. 少字 ...
- Python 3.X简史——记录3.0之后的重要更新
Python 3.0在2008年12月3日正式发布,在之后又经历了多个小版本(3.1,3.2,3.3……),本文梳理Python 3.0之后的新特性. 其实每个版本都有大量更新,都写出来要几百页,这里 ...
- 【英语魔法俱乐部——读书笔记】 3 高级句型-简化从句&倒装句(Reduced Clauses、Inverted Sentences) 【完结】
[英语魔法俱乐部——读书笔记] 3 高级句型-简化从句&倒装句(Reduced Clauses.Inverted Sentences):(3.1)从属从句简化的通则.(3.2)形容词从句简化. ...
随机推荐
- 20155238 2016-2017-2 《JAVA程序设计》第十周学习总结
教材学习内容总结 # Java计算机网络基础 计算机网络 计算机网络是通过传输介质.通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来,实现资源共享和数据传输的系统.网络编程就就是编写程序使 ...
- 20155339 Exp5 MSF基础应用
20155339 Exp5 MSF基础应用 基础问题回答 用自己的话解释什么是exploit,payload,encode. 答: exploit是通过自己选择的漏洞和载荷进行攻击的一个过程. pay ...
- 20155339 Exp3 免杀原理与实践
20155339 Exp3 免杀原理与实践 基础问题 (1)杀软是如何检测出恶意代码的? 基于特征码的检测(杀软的特征库中包含了一些数据或者数据段,杀软会尽可能的更新这个特征库,以包括尽可能多的恶意代 ...
- Data Consistency Primer
云应用通常来说,使用的数据很多都是分散的,来自不同的数据仓库.在这种环境下,管理和保持数据一致性是很复杂的,无论是在并发跟可用性上都可能出问题.开发者有的时候就需要为了强一致性而牺牲可用性了.这也就意 ...
- 【php增删改查实例】第一节 - PHP开发环境配置
最近需要使用PHP,于是把平时的积累整理一下,就有了这个教程. 首先是环境配置: 1.操作系统:windos7 2.后台:PHP 3.前台:Html + js + css 4.数据库:MYSQL 5. ...
- 蓝牙重启case之:hardware error
蓝牙的通信分为host和controller,host端发送数据和命令到controller,controller 上传event以及数据到host端,这要求上下两端的通信要求状态一致性. 当发生状态 ...
- [arc076F]Exhausted?[霍尔定理+线段树]
题意 地上 \(1\) 到 \(m\) 个位置摆上椅子,有 \(n\) 个人要就座,每个人都有座位癖好:选择 \(\le L\) 或者 \(\ge R\) 的位置.问至少需要在两边添加多少个椅子能让所 ...
- 用C++实现一个Brainfuck解释器
Brainfuck是一种极小化的计算机语言,只含有8种运算符,由于fuck在英语中是脏话,这种语言有时被称为brainfck或brainf**,甚至被简称为BF.正如它的名字所暗示,brainfuck ...
- tcp ,http .udp
三次握手,四次挥手要知道,记住. 计算机协议常见面试题,学会了,记住.会运用.
- PAT甲题题解-1071. Speech Patterns (25)-找出现最多的单词
分割字符串的用法+map映射给出input中出现次数最多的单词,如果次数相同,给出按字典序最小的. 这里我用了自定义分隔符来读取字符串,方法如下: //按照定义的分隔符d来分割字符串,对str进行读取 ...