Welfare State
There is a country with nn citizens. The ii-th of them initially has aiai money. The government strictly controls the wealth of its citizens. Whenever a citizen makes a purchase or earns some money, they must send a receipt to the social services mentioning the amount of money they currently have.
Sometimes the government makes payouts to the poor: all citizens who have strictly less money than xx are paid accordingly so that after the payout they have exactly xx money. In this case the citizens don't send a receipt.
You know the initial wealth of every citizen and the log of all events: receipts and payouts. Restore the amount of money each citizen has after all events.
Input
The first line contains a single integer n (1≤n≤2⋅10^5) — the numer of citizens.
The next line contains nn integers a1, a2, ..., an (0≤ai≤10^9) — the initial balances of citizens.
The next line contains a single integer q (1≤q≤2⋅105) — the number of events.
Each of the next qq lines contains a single event. The events are given in chronological order.
Each event is described as either 1 p x (1≤p≤n, 0≤x≤10^9), or 2 x (0≤x≤10^9). In the first case we have a receipt that the balance of the p-th person becomes equal to x. In the second case we have a payoff with parameter x.
Output
Print nn integers — the balances of all citizens after all events.
Examples
input
4
1 2 3 4
3
2 3
1 2 2
2 1
output
3 2 3 4
链接:https://codeforces.com/contest/1198/problem/B
题意:操作为1时,将第x个数改为v;操作为2时,将小于v的所有数改成v。
题解:操作1为线段树单点更新,操作2为线段树区间更新,本题与其他线段树相比,没有求和操作,所以更加偏向于单点更新,对于操作2区间更新来说,无论以前存的什么值,只要比我v小的都要换成v,所以在线段树根节点维护一个最小值,如果最小值小于v,说明线段树需要进行区间更新,加上懒标记即可。在操作1单点更新时,直接logn去更新,顺便把区间更新的懒标记推下去。
#include <iostream>
using namespace std;
struct node
{
long long sum,mark;
}tree[200005*4];
long long a[200005];
long long read()
{
char ch=getchar();
long long x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
void build(int l,int r,int i)//建树
{
if(l==r)
{
tree[i].sum=read();//叶节点存值
return;
}
int mid=(l+r)/2;
build(l,mid,i+i);
build(mid+1,r,i+i+1);
tree[i].sum=min(tree[i+i].sum,tree[i+i+1].sum);
}
void down(int l,int r,int i)
{
if(tree[i].mark)
{
tree[i+i].mark=max(tree[i].mark,tree[i+i].mark);
tree[i+i+1].mark=max(tree[i].mark,tree[i+i+1].mark);
tree[i+i].sum=max(tree[i+i].sum,tree[i].mark);
tree[i+i+1].sum=max(tree[i+i+1].sum,tree[i].mark);
tree[i].mark=0;
}
}
void update2(int l,int r,int i,long long v)//区间更新
{
if(tree[i].sum>=v)
return;
else
{
tree[i].sum=max(tree[i].sum,v);
tree[i].mark=max(tree[i].mark,v);
}
}
void update1(int l,int r,int i,int x,long long v)//单点更新
{
if(l==r)
{
tree[i].sum=v;
return;
}
else
{
down(l,r,i);
int mid=(l+r)/2;
if(x<=mid)
update1(l,mid,i+i,x,v);
else
update1(mid+1,r,i+i+1,x,v);
tree[i].sum=min(tree[i+i].sum,tree[i+i+1].sum);
}
}
void query(int l,int r,int i)
{
if(l==r)
{
printf("%d ",tree[i].sum);
return;
}
down(l,r,i);
int mid=(l+r)/2;
query(l,mid,i+i);
query(mid+1,r,i+i+1);
}
int main()
{
int n,q,op,x;
long long v;
scanf("%d",&n);
build(1,n,1);
scanf("%d",&q);
while(q--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&x); v=read();
update1(1,n,1,x,v); //单点更新
}
else
{
v=read();
update2(1,n,1,v);//区间更新
}
}
query(1,n,1);
return 0;
}
第二种方法:(听说是离线+思维)
#include <iostream>
using namespace std;
long long a[200005];
long long op2[200005];
long long op1[200005];
long long n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%I64d",&a[i]);
int d;
scanf("%d",&d);
int p,x;
long long v;
for(int i=1;i<=d;i++)
{
scanf("%d",&p);
if(p==1)
{
scanf("%d%I64d",&x,&v);
a[x]=v;
op1[x]=i;
}
else
{
scanf("%I64d",&v);
op2[i]=v;
}
}
for(int i=d;i>=0;i--)
op2[i]=max(op2[i],op2[i+1]);
for(int i=1;i<=n;i++)
{
if(a[i]<op2[op1[i]+1])
a[i]=op2[op1[i]+1];
}
for(int i=1;i<=n;i++)
printf("%I64d ",a[i]);
return 0;
}