考试 线段树二分+单点修改+区间查询
2016-08-27 07:50
441 查看
Problem I
定义Mex({al,al+1,al+2,...,ar})为其中最小未出现的自然数
给出一个长度为n的序列a1,a2,a3,...,an
求所有区间[L,R]的Mex值之和
第一行包含一个整数n(1<=n<=1e5)
第二行包含n个整数,表示a1,a2,...,an(1<=ai<=1e5)
输出Mex值之和
Sample Input
5
1 0 2 0 1
Sample Output
24
虽然在考试的时候是A掉的,但是我局的我的线段树的二分写的是非常的脑残,极其不优美所以我觉的还是很有必要总结一下的。
其实我忽略了一个事实就是在有序数列中二分的时候,只用知道右区间的最大值就可以知道接下来的去向,而并不需要用到其他的值,所以只需要再维护一个区间的最大值就可以进行二分了,其实我的代码也是这样做的,只是前面加了一些莫名其妙的奇奇怪怪的我也不知道是脑残了还是什么鬼的东西,总之下一次不要再犯错了。
至于这一道题目本身我觉得没啥好讲的,打个表规律就出来了,接下来就看你的造化了
我的(因为维护了一些奇奇怪怪的东西显得很冗长我很不喜欢):#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
#define maxn 100020
#define ls u<<1,l,mid
#define rs u<<1|1,mid+1,r
using namespace std;
void init(){
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
}
int n,cur[maxn],front[maxn];
bool vis[maxn];
struct node{
int l,r,sum,add,rMin,rMax,lMin,lMax,lazy;
}nod[maxn*4];
int head[maxn],last[maxn],tot=1;
struct dege{
int v,next;
}e[maxn*2];
void adde(int a,int b){
e[tot].v=b;
e[tot].next=head[a];
head[a]=tot++;
}
void push_up(int u){
nod[u].sum=nod[u<<1].sum+nod[u<<1|1].sum;
nod[u].rMin=nod[u<<1|1].lMin;nod[u].lMax=nod[u<<1].rMax;
nod[u].rMax=nod[u<<1|1].rMax,nod[u].lMin=nod[u<<1].lMin;
}
void push_down(int u){
if(nod[u].lazy==0)return;
int add=nod[u].add;
nod[u<<1].add=nod[u<<1|1].add=add;
nod[u<<1].lazy=nod[u<<1|1].lazy =1;
nod[u<<1].sum=(nod[u<<1].r-nod[u<<1].l+1)*add;
nod[u<<1|1].sum=(nod[u<<1|1].r-nod[u<<1|1].l+1)*add;
nod[u<<1].rMax=nod[u<<1].rMin=nod[u<<1].lMin=nod[u<<1].lMax=add;
nod[u<<1|1].rMax=nod[u<<1|1].rMin=nod[u<<1|1].lMin=nod[u<<1|1].lMax=add;
nod[u].add=0;
nod[u].lazy=0;
}
void build(int u,int l,int r){
nod[u].l=l,nod[u].r=r;
if(l==r){
nod[u].lMin=nod[u].lMax=nod[u].rMin=nod[u].rMax=nod[u].sum=front[l];
nod[u].add=nod[u].lazy=0;
return ;
}
int mid=l+r>>1;
build(ls);build(rs);
push_up(u);
}
int query_max(int u,int l,int r,int x){
if(l==r)return r;
push_down(u);
push_up(u);
int mid=l+r>>1,rr=u<<1|1;
if(nod[u<<1].rMax<=x)return query_max(rs,x);
if(nod[u<<1].rMax>x)return query_max(ls,x);
}
void update(int u,int l,int r,int x,int y,int add){
if(x==l&&r==y){
nod[u].sum=(r-l+1)*add;
nod[u].add=add;
nod[u].lazy=1;
nod[u].rMax=nod[u].rMin=nod[u].lMin=nod[u].lMax=add;
return;
}
push_down(u);
int mid=l+r>>1;
if(x>mid)update(rs,x,y,add);
else if(y<=mid)update(ls,x,y,add);
else{
update(ls,x,mid,add);
update(rs,mid+1,y,add);
}
push_up(u);
}
int query_z(int u,int l,int r,int x){
if(l==r)return nod[u].sum ;
push_down(u);
int mid=l+r>>1;
if(x>mid)return query_z(rs,x);
else return query_z(ls,x);
}
int main(){
init();
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",cur+i);
if(!last[cur[i]])last[cur[i]]=i;
else{
adde(last[cur[i]],i);
last[cur[i]]=i;
}
}
int last=0;
for(int i=1;i<=n;i++){
vis[cur[i]]=true;
while(vis[last])last++;
front[i]=last;
}
ll ans=0;
build(1,1,n);
for(int i=1;i<n;i++){
ans+=(ll)nod[1].sum;
int y,x;
if(head[i]==-1)y=n;
else y=e[head[i]].v-1;
update(1,1,n,i,i,0);
x=query_max(1,1,n,cur[i]);
if(x==y&&x==n){
int z=query_z(1,1,n,n);
if(z<=cur[i])continue;
}
if(x<=y)update(1,1,n,x,y,cur[i]);
}
printf("%I64d",ans);
return 0;
}
改进后的,不过在我这种空格满天飞的代码面前还是比较长,但是不冗杂:
定义Mex({al,al+1,al+2,...,ar})为其中最小未出现的自然数
给出一个长度为n的序列a1,a2,a3,...,an
求所有区间[L,R]的Mex值之和
第一行包含一个整数n(1<=n<=1e5)
第二行包含n个整数,表示a1,a2,...,an(1<=ai<=1e5)
输出Mex值之和
Sample Input
5
1 0 2 0 1
Sample Output
24
虽然在考试的时候是A掉的,但是我局的我的线段树的二分写的是非常的脑残,极其不优美所以我觉的还是很有必要总结一下的。
其实我忽略了一个事实就是在有序数列中二分的时候,只用知道右区间的最大值就可以知道接下来的去向,而并不需要用到其他的值,所以只需要再维护一个区间的最大值就可以进行二分了,其实我的代码也是这样做的,只是前面加了一些莫名其妙的奇奇怪怪的我也不知道是脑残了还是什么鬼的东西,总之下一次不要再犯错了。
至于这一道题目本身我觉得没啥好讲的,打个表规律就出来了,接下来就看你的造化了
我的(因为维护了一些奇奇怪怪的东西显得很冗长我很不喜欢):#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
#define maxn 100020
#define ls u<<1,l,mid
#define rs u<<1|1,mid+1,r
using namespace std;
void init(){
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
}
int n,cur[maxn],front[maxn];
bool vis[maxn];
struct node{
int l,r,sum,add,rMin,rMax,lMin,lMax,lazy;
}nod[maxn*4];
int head[maxn],last[maxn],tot=1;
struct dege{
int v,next;
}e[maxn*2];
void adde(int a,int b){
e[tot].v=b;
e[tot].next=head[a];
head[a]=tot++;
}
void push_up(int u){
nod[u].sum=nod[u<<1].sum+nod[u<<1|1].sum;
nod[u].rMin=nod[u<<1|1].lMin;nod[u].lMax=nod[u<<1].rMax;
nod[u].rMax=nod[u<<1|1].rMax,nod[u].lMin=nod[u<<1].lMin;
}
void push_down(int u){
if(nod[u].lazy==0)return;
int add=nod[u].add;
nod[u<<1].add=nod[u<<1|1].add=add;
nod[u<<1].lazy=nod[u<<1|1].lazy =1;
nod[u<<1].sum=(nod[u<<1].r-nod[u<<1].l+1)*add;
nod[u<<1|1].sum=(nod[u<<1|1].r-nod[u<<1|1].l+1)*add;
nod[u<<1].rMax=nod[u<<1].rMin=nod[u<<1].lMin=nod[u<<1].lMax=add;
nod[u<<1|1].rMax=nod[u<<1|1].rMin=nod[u<<1|1].lMin=nod[u<<1|1].lMax=add;
nod[u].add=0;
nod[u].lazy=0;
}
void build(int u,int l,int r){
nod[u].l=l,nod[u].r=r;
if(l==r){
nod[u].lMin=nod[u].lMax=nod[u].rMin=nod[u].rMax=nod[u].sum=front[l];
nod[u].add=nod[u].lazy=0;
return ;
}
int mid=l+r>>1;
build(ls);build(rs);
push_up(u);
}
int query_max(int u,int l,int r,int x){
if(l==r)return r;
push_down(u);
push_up(u);
int mid=l+r>>1,rr=u<<1|1;
if(nod[u<<1].rMax<=x)return query_max(rs,x);
if(nod[u<<1].rMax>x)return query_max(ls,x);
}
void update(int u,int l,int r,int x,int y,int add){
if(x==l&&r==y){
nod[u].sum=(r-l+1)*add;
nod[u].add=add;
nod[u].lazy=1;
nod[u].rMax=nod[u].rMin=nod[u].lMin=nod[u].lMax=add;
return;
}
push_down(u);
int mid=l+r>>1;
if(x>mid)update(rs,x,y,add);
else if(y<=mid)update(ls,x,y,add);
else{
update(ls,x,mid,add);
update(rs,mid+1,y,add);
}
push_up(u);
}
int query_z(int u,int l,int r,int x){
if(l==r)return nod[u].sum ;
push_down(u);
int mid=l+r>>1;
if(x>mid)return query_z(rs,x);
else return query_z(ls,x);
}
int main(){
init();
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",cur+i);
if(!last[cur[i]])last[cur[i]]=i;
else{
adde(last[cur[i]],i);
last[cur[i]]=i;
}
}
int last=0;
for(int i=1;i<=n;i++){
vis[cur[i]]=true;
while(vis[last])last++;
front[i]=last;
}
ll ans=0;
build(1,1,n);
for(int i=1;i<n;i++){
ans+=(ll)nod[1].sum;
int y,x;
if(head[i]==-1)y=n;
else y=e[head[i]].v-1;
update(1,1,n,i,i,0);
x=query_max(1,1,n,cur[i]);
if(x==y&&x==n){
int z=query_z(1,1,n,n);
if(z<=cur[i])continue;
}
if(x<=y)update(1,1,n,x,y,cur[i]);
}
printf("%I64d",ans);
return 0;
}
改进后的,不过在我这种空格满天飞的代码面前还是比较长,但是不冗杂:
#include<cstdio> #include<cstring> #include<iostream> #define ll long long #define maxn 100020 #define ls u<<1,l,mid #define rs u<<1|1,mid+1,r using namespace std; void init(){ freopen("A.in","r",stdin); freopen("A.out","w",stdout); } int n,cur[maxn],front[maxn]; bool vis[maxn]; struct node{ int l,r,sum,add,Max,lazy; }nod[maxn*4]; int head[maxn],last[maxn],tot=1; struct dege{ int v,next; }e[maxn*2]; void adde(int a,int b){ e[tot].v=b; e[tot].next=head[a]; head[a]=tot++; } void push_up(int u){ nod[u].sum=nod[u<<1].sum+nod[u<<1|1].sum; nod[u].Max=max(nod[u<<1].Max,nod[u<<1|1].Max); } void push_down(int u){ if(nod[u].lazy==0)return; int add=nod[u].add; nod[u<<1].add=nod[u<<1|1].add=add; nod[u<<1].lazy=nod[u<<1|1].lazy =1; nod[u<<1].sum=(nod[u<<1].r-nod[u<<1].l+1)*add; nod[u<<1|1].sum=(nod[u<<1|1].r-nod[u<<1|1].l+1)*add; nod[u<<1].Max=add; nod[u<<1|1].Max=add; nod[u].add=0;nod[u].lazy=0; } void build(int u,int l,int r){ nod[u].l=l,nod[u].r=r; if(l==r){ nod[u].Max=nod[u].sum=front[l]; nod[u].add=nod[u].lazy=0; return ; } int mid=l+r>>1; build(ls);build(rs); push_up(u); } int query_max(int u,int l,int r,int x){ if(l==r)return nod[u].sum>x?l:l+1; push_down(u); int mid=l+r>>1; if(nod[u<<1].Max<=x)return query_max(rs,x); if(nod[u<<1].Max>x)return query_max(ls,x); } void update(int u,int l,int r,int x,int y,int add){ if(x==l&&r==y){ nod[u].sum=(r-l+1)*add; nod[u].add=add;nod[u].lazy=1; nod[u].Max=add; return; } push_down(u); int mid=l+r>>1; if(x>mid)update(rs,x,y,add); else if(y<=mid)update(ls,x,y,add); else update(ls,x,mid,add),update(rs,mid+1,y,add); push_up(u); } int main(){ init(); memset(head,-1,sizeof(head)); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",cur+i); if(!last[cur[i]])last[cur[i]]=i; else{ adde(last[cur[i]],i); last[cur[i]]=i; } } int last=0; for(int i=1;i<=n;i++){ vis[cur[i]]=true; while(vis[last])last++; front[i]=last; } ll ans=0; build(1,1,n); for(int i=1;i<n;i++){ ans+=(ll)nod[1].sum; int y,x; if(head[i]==-1)y=n; else y=e[head[i]].v-1; update(1,1,n,i,i,0); x=query_max(1,1,n,cur[i]); if(x<=y)update(1,1,n,x,y,cur[i]); } printf("%I64d",ans); return 0; }
相关文章推荐
- poj2886线段树(单点修改,区间查询)
- 【数据结构】【线段树】单点修改区间查询
- 【codevs 1081】线段树练习2(单点查询+区间修改)
- HDU1166 线段树 单点修改、区间查询
- HDU 4893 Wow! Such Sequence! (线段树单点更新+区间更新+区间查询+二分)
- 线段树——区间查询+单点修改
- BZOJ-1036: [ZJOI2008]树的统计Count (树链剖分 线段树 单点修改 区间查询 入门题)
- UVA - 12299 RMQ with Shifts (线段树:单点修改,区间查询)
- HDU 5861 Road(线段树 区间修改 单点查询)
- hdu 1754 线段树单点修改+区间查询
- 树套树:二维线段树初步:hdu1823——Luck and Love(单点修改,区间查询)
- HDU3974 线段树区间修改单点查询+树上dfs序
- Tallest Cow POJ - 3263(线段树区间修改,单点查询)
- 【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询
- 喵哈哈村的魔法考试 Round #2 (Div.2) B.喵哈哈村的种花魔法 线段树 区间更新 单点查询
- HDU1166_敌兵布阵_线段树单点修改区间查询
- POJ 3321 Apple Tree(DFS序+线段树单点修改区间查询)
- 线段树(区间修改,单点查询)
- POJ 2155 Matrix 二维线段树 区间修改 单点查询
- Wikilo 1191线段树区间修改单点查询