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)形容词从句简化. ...
随机推荐
- Java实现Zip压缩包解压
对zip压缩包的解压是比较常见的应用场景,java代码的实现也很简单.废话不多说,直接上代码吧 一.代码 /** * zip解压 * @param srcFile zip源文件 * @para ...
- EZ 2018 04 21 NOIP2018 模拟赛(十) -LoliconAutomaton的退役赛
难得的一次Unrated,避免了重回1500的尴尬 其实题目都还可以,但只不过所有人T1都炸了,可能是数据的锅(假的) 而且我因为T1SB的把T2弃了,没想到是千年水题 T3莫名爆炸,然后TM的40分 ...
- OFS环境,删除Resource 时出现错误失败,应该如何继续
From the Windows failover cluster manager,select the group listener, stop it, and delete it. Do the ...
- PostgreSQL同步方案
Windows下Postgre SQL数据库通过Slony-I实现数据库双机同步备份 - 数据库其他综合 - 红黑联盟 postgresql同步流复制的Hot Standby - CSDN博客 使 ...
- ESLint 规则详解(二)
接上篇 ESLint 规则详解(一) 前端界大神 Nicholas C. Zakas 在 2013 年开发的 ESLint,极大地方便了大家对 Javascript 代码进行代码规范检查.这个工具包含 ...
- 一种C#泛型方法在lua中表示的设计
在进行lua方法注册的时候, 大多数解决方案直接否定了泛型方法, 因为在lua侧难以表达出泛型, 以及lua的函数重载问题, 函数重载问题可以通过一些特殊方法解决, 而泛型问题是主要问题, 以Unit ...
- 关于T/G/M/K
//扫盲贴 K, G, T,都是表数量,只是个数字,在不同的场合下表示的不同.在计算机行业中,这几个量可用来表示数据传输速度和容量,下面分别讨论,希望不了解的朋友不要被某知道上的误解了.如果有什么错误 ...
- 分分钟让你理解HTTPS
一.HTTP存在的问题 1.1 可能被窃听 HTTP 本身不具备加密的功能,HTTP 报文使用明文方式发送 由于互联网是由联通世界各个地方的网络设施组成,所有发送和接收经过某些设备的数据都可能被截获或 ...
- maven util 类 添加 service
直接关键代码: public class DictionaryUtil { // 以下的处理,是为了在工具类中自动注入service // 前提是在applicationContext.xml中,将该 ...
- Python中的字典详解
https://www.cnblogs.com/yjd_hycf_space/p/6880026.html