网址:http://codeforces.com/problemset/problem/1198/B
题意:
给出初始序列,规定两种操作:$1$ $p$ $x$代表把第$p$个值改成$x$,$2$ $x$代表把小于$x$的值都变成$x$,大于$x$的不变。求所有操作完成后的序列。($n ,m \leq 2e5 $)。
题解:
一、线段树版本:
操作即为典型的单点修改、区间修改和单点查询。lazy标记记录区间修改的值,由于$2$操作中大的$x$值一定会覆盖小的$x$值,故lazy标记下推时取较大值,单点修改时下推标记,由于单点修改是最高级的,所以每一次单点修改一定会清空目标叶节点的区间修改,然后单点查询时取lazy和叶节点值较大那个作为最终值。
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define ls (k<<1)
#define rs (k<<1)+1
const int MAXBUF = 1 << 20;
char buf[1<<20], *fh=NULL, *ft=NULL;
inline char gc()
{
if(fh == ft)
{
int l = fread(buf, 1, MAXBUF, stdin);
ft = (fh = buf) + l;
}
return *fh++;
}
inline int read()
{
int x = 0;
char c = gc();
for(; c < '0' || c > '9'; c = gc())
;
for(; c >= '0' && c <= '9'; c = gc())
x = (x << 3) + (x << 1) + c - '0';
return x ;
}
inline void _write(long long x)
{
if(x > 9)
_write(x / 10);
putchar(x % 10 + '0');
}
inline void write(long long x)
{
_write(x);
//putchar('\n');
}
struct segtree
{
struct node
{
int l,r,val,lazy;
};
node tr[MAXN<<2];
void build(int l,int r,int k)
{
tr[k].l=l,tr[k].r=r;
tr[k].val=tr[k].lazy=0;
if(l==r)
return;
int m=(l+r)>>1;
build(l,m,ls);
build(m+1,r,rs);
}
void down(int k)
{
tr[ls].lazy=max(tr[k].lazy,tr[ls].lazy);
tr[rs].lazy=max(tr[k].lazy,tr[rs].lazy);
tr[k].lazy=0;
}
void update1(int pos,int k,int val)
{
if(tr[k].l==tr[k].r)
{
tr[k].lazy=0;
tr[k].val=val;
return;
}
down(k);
int m=(tr[k].l+tr[k].r)>>1;
if(pos<=m)
update1(pos,ls,val);
else
update1(pos,rs,val);
tr[k].val=min(tr[ls].val,tr[rs].val);
}
void update2(int l,int r,int k,int val)
{
if(l<=tr[k].l&&tr[k].r>=tr[k].r)
{
tr[k].lazy=max(tr[k].lazy,val);
return;
}
down(k);
int m=(tr[k].l+tr[k].r)>>1;
if(l<=m&&val>tr[ls].val)
update2(l,r,ls,val);
if(r>m&&val>tr[rs].val)
update2(l,r,rs,val);
tr[k].val=min(tr[ls].val,tr[rs].val);
}
int query(int pos,int k)
{
if(tr[k].l==tr[k].r)
return max(tr[k].val,tr[k].lazy);
down(k);
int m=(tr[k].l+tr[k].r)>>1;
if(pos<=m)
return query(pos,ls);
else
return query(pos,rs);
}
};
segtree tr;
int main()
{
int n,m,a,p,x;
//scanf("%d",&n);
n=read();
tr.build(1,n,1);
for(int i=1;i<=n;++i)
{
a=read();
tr.update1(i,1,a);
}
m=read();
for(int i=0;i<m;++i)
{
a=read();
p=read();
if(a==1)
{
x=read();
tr.update1(p,1,x);
}
else
tr.update2(1,n,1,p);
}
for(int i=1;i<=n;++i)
{
write(tr.query(i,1));
putchar((i==n?'\n':' '));
}
return 0;
}
二、单调栈+二分解法:
使用单调递减栈记录区间修改操作,然后对于每一个元素,单点修改直接在原数组中修改,记录最后一次修改时间,然后在栈中二分查找它的最后一次单点修改的时间的后面最大的一个区间修改(就是其后面第一个区间修改),然后两个取最大值。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=200005;
int sta[MAXN],top=0,num[MAXN],lstch[MAXN],add[MAXN];
const int MAXBUF = 1 << 20;
char buf[1<<20], *fh=NULL, *ft=NULL;
inline char gc()
{
if(fh == ft)
{
int l = fread(buf, 1, MAXBUF, stdin);
ft = (fh = buf) + l;
}
return *fh++;
}
inline void read(int &x)
{
x = 0;
char c = gc();
for(; c < '0' || c > '9'; c = gc())
;
for(; c >= '0' && c <= '9'; c = gc())
x = (x << 3) + (x << 1) + c - '0';
return;
}
inline void _write(long long x)
{
if(x > 9)
_write(x / 10);
putchar(x % 10 + '0');
}
inline void write(long long x)
{
_write(x);
//putchar('\n');
}
int main()
{
int n,m,a,p,x;
//scanf("%d",&n);
read(n);
for(int i=1;i<=n;++i)
read(num[i]);
//scanf("%d",&num[i]);
//scanf("%d",&m);
read(m);
for(int i=1;i<=m;++i)
{
read(a);
//scanf("%d",&a);
read(p);
//scanf("%d",&p);
if(a==1)
{
read(x);
//scanf("%d",&x);
num[p]=x;
lstch[p]=i;
}
else
{
while(top&&p>=sta[top])
--top;
sta[++top]=p;
add[top]=i;
}
}
sta[++top]=0;
add[top]=m+1;
for(int i=1;i<=n;++i)
num[i]=max(num[i],sta[lower_bound(add+1,add+top+1,lstch[i])-add]);
for(int i=1;i<=n;++i)
write(num[i]),putchar(i==n?'\n':' ');
return 0;
}