【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)$
#include<bits/stdc++.h>
#define D long double
#define M (1<<18)
#define lowbit(x) (x&(-x))
#define N 20000005
#define S set<node>::iterator
#define eps (1e-8)
using namespace std; int n,m; D c[M]={}; void add(int x,D k){for(int i=x;i<=n;i+=lowbit(i)) c[i]+=k;}
D query(int x){D k=; for(int i=x;i;i-=lowbit(i)) k+=c[i]; return k;}
int num[M]={}; D val[M]={}; struct node{
int l,r,rt,op;
node(int ll=,int rr=,int RT=,int OP=){l=ll; r=rr; rt=RT; op=OP;}
friend bool operator <(node a,node b){return a.l<b.l;}
};set<node> s; int lc[N]={},rc[N]={},cnt[N]={},use=; D sum[N]={};
void pushup(int x){
cnt[x]=cnt[lc[x]]+cnt[rc[x]];
sum[x]=sum[lc[x]]+sum[rc[x]];
}
int merge(int x,int y){
if(!x||!y) return x|y;
lc[x]=merge(lc[x],lc[y]);
rc[x]=merge(rc[x],rc[y]);
cnt[x]=cnt[x]+cnt[y];
sum[x]=sum[x]+sum[y];
return x;
} S Ins(node x){add(x.l,sum[x.rt]);return s.insert(x).first;}
void Del(S it){add(it->l,-sum[it->rt]);s.erase(it);} void split(int x,int &rt1,int &rt2,int l,int r,int k){
rt1=++use; rt2=++use;
if(l==r){
cnt[rt1]=k; sum[rt1]=val[l]*k;
cnt[rt2]=cnt[x]-cnt[rt1];
sum[rt2]=sum[x]-sum[rt1];
return;
}
int mid=(l+r)>>;
if(cnt[lc[x]]>=k){
rc[rt2]=rc[x];
split(lc[x],lc[rt1],lc[rt2],l,mid,k);
}else{
lc[rt1]=lc[x];
split(rc[x],rc[rt1],rc[rt2],mid+,r,k-cnt[lc[x]]);
}
pushup(rt1); pushup(rt2);
}
S split(int x){
if(x>n) return s.end();
S it=s.upper_bound(node(x,,,)); it--;
node hh=*it; if(hh.l==x) return it;
int rt1,rt2;
if(!hh.op) split(hh.rt,rt1,rt2,,n,x-hh.l);
else split(hh.rt,rt2,rt1,,n,hh.r-x+);
Del(it);
Ins(node(hh.l,x-,rt1,hh.op));
return Ins(node(x,hh.r,rt2,hh.op));
} void updata(int l,int r,int op){
S L=split(l); split(r+); int rt=;
for(S it=L;it!=s.end()&&(it->l)<=r;Del(it++)){
rt=merge(rt,it->rt);
}
Ins(node(l,r,rt,op));
}
int calc(int l,int r){
S L=split(l),R=split(r+); R--;
D ans=query(R->r)-query(L->l-);
D out=pow(,ans-floorl(ans)+eps);
return floorl(out);
} void build(int &x,int l,int r,int k){
x=++use; cnt[x]++; sum[x]+=val[k];
if(l==r) return; int mid=(l+r)>>;
if(k<=mid) build(lc[x],l,mid,k);
else build(rc[x],mid+,r,k);
} int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) scanf("%d",num+i);
for(int i=;i<=n;i++) val[i]=log10(i);
for(int i=;i<=n;i++){
int now; build(now,,n,num[i]);
Ins(node(i,i,now,));
}
while(m--){
int op,l,r; scanf("%d%d%d",&op,&l,&r);
if(op==) printf("%d\n",calc(l,r));
else{
scanf("%d",&op); op^=;
updata(l,r,op);
}
}
}
【xsy2194】Philosopher set+线段树合并的更多相关文章
- [XJOI NOI2015模拟题13] C 白黑树 【线段树合并】
题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动 ...
- [BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】
题目链接:BZOJ - 2212 题目分析 子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对. 左右子树内部的逆序对与是否交换 ...
- BZOJ 3307: 雨天的尾巴( LCA + 线段树合并 )
路径(x, y) +z : u处+z, v处+z, lca(u,v)处-z, fa(lca)处-z, 然后dfs一遍, 用线段树合并. O(M log M + M log N). 复杂度看起来不高, ...
- BZOJ2733 [HNOI2012]永无乡 【线段树合并】
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- bzoj 2243 [SDOI2011]染色(树链剖分+线段树合并)
[bzoj2243][SDOI2011]染色 2017年10月20日 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询 ...
- bzoj3702二叉树 线段树合并
3702: 二叉树 Time Limit: 15 Sec Memory Limit: 256 MBSubmit: 600 Solved: 272[Submit][Status][Discuss] ...
- BZOJ_2212_[Poi2011]Tree Rotations_线段树合并
BZOJ_2212_[Poi2011]Tree Rotations_线段树合并 Description Byteasar the gardener is growing a rare tree cal ...
- B20J_2733_[HNOI2012]永无乡_权值线段树合并
B20J_2733_[HNOI2012]永无乡_权值线段树合并 Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示.某些 ...
- BZOJ_3307_雨天的尾巴_线段树合并+树上差分
BZOJ_3307_雨天的尾巴_线段树合并 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后 ...
随机推荐
- 2018.12.15 poj3415 Common Substrings(后缀自动机)
传送门 后缀自动机基础题. 给两个字符串,让你求长度不小于kkk的公共子串的数量. 这题可以用后缀自动机解决废话 考虑对其中一个字串建出后缀自动机,然后用另一个在上面跑,注意到如果一个状态有贡献的话, ...
- 微信小程序toast框的使用
1.wx.showToast() 方法可以配置toast框的提示文字,消失的时间,显示的图标 wx.showToast({ title: '请链接网络', icon:"none", ...
- s5-11 距离矢量路由选择协议
距离矢量路由选择(Distance Vector:DV) 每个路由器维护一张表,表中列出了当前已知的到每个目标 的最佳距离,以及为了到达那个目标,应该从哪个接口转发. 距离矢量路由选择(Distanc ...
- abaqus学习笔记-abaqus与umat调用基本原理
参考: 1.http://ivt-abaqusdoc.ivt.ntnu.no:2080/v6.14/books/sub/default.htm 2.ABAQUS 用户材料子程序开发及应用-杨曼娟 3. ...
- 第17章:MongoDB-聚合操作--聚合管道--$group
①$group 作用:将集合中的文档进行分组,可用于统计结果. 例如: db.scores.aggregate({“$group”:{“_id”:“$studentId”}}); 或者是 db.sco ...
- linux 定时器 定时执行php
输入命令: crontab -e 添加定时命令 每隔一分钟执行一次php //复制一下命令即可. */1 * * * * /usr/bin/curl -o /home/logs/temp.lo ...
- load data妙用
load变量和用户变量的巧妙结合,实现灵活导入字段列(NO.1) LOAD DATA INFILE 'file.csv' INTO TABLE dados_meteo (@var1, @var2) S ...
- npp 文本编辑器 开源
下载地址 https://notepad-plus-plus.org/download/v6.8.4.html
- spring之hello(简单环境配置)
导入java包 配置springmvc.xml文件 <?xml version="1.0" encoding="UTF-8"?> <beans ...
- (暴力 记录)Camellia的难题 -- zzuli -- 1784
http://acm.zzuli.edu.cn/problem.php?id=1784 Camellia的难题 Time Limit: 2 Sec Memory Limit: 128 MBSubmi ...