説明文
国民がn人いる国があります。それらのi番目は最初にaiお金を持っています。政府は国民の富を厳しく管理しています。市民が購入したり、お金を稼いだりするときはいつでも、彼らは現在持っているお金の量を述べた領収書を社会福祉サービスに送る必要があります。
政府は貧しい人々に支払いを行う場合があります。厳密にxよりもお金が少ないすべての市民はそれに応じて支払いを受け、支払い後に正確にxのお金を手にします。この場合、市民は領収書を送信しません。
あなたはすべての市民の最初の富とすべてのイベントのログを知っています:領収書と支払い。すべてのイベントの後に、各市民が持っている金額を元に戻します。
入力
最初の行には単一の整数n (市民の数)が含まれています。
次の行にはn個の整数 、つまり市民の初期バランスが含まれています 。
次の行には、単一の整数q (イベントの数)が含まれてい ます。
次のq行にはそれぞれ1つのイベントが含まれています。イベントは時系列で表示されます。
各イベントは1 px または2 x として記述されます。最初のケースでは、pp番目の人の残高がxに等しくなるというレシートがあります。2番目のケースでは、パラメーターxのペイオフがあります。
出力
n個の整数を出力します—すべてのイベント後のすべての市民のバランス。
例
入力
4
1 2 3 4
3
2 3
1 2 2
2 1
出力
3 2 3 4
入力
5
3 50 2 1 10
3
1 2 0
2 8
1 3 20
出力
8 8 20 8 10
注意
最初の例では、残高は次のように変化します。1 2 3 4→→3 3 3 4→→3 2 3 4→→3 2 3 4
2番目の例では、残高は次のように変化します。3 50 2 1 10→→3 0 2 1 10→→8 8 8 8 10→→8 8 20 8 10
主なアイデア:
データの文字列があり、このデータの文字列に対して次の2つの操作を実行できます。
1.任意の場所でデータを変更します。
2.データの一部を指定して、この番号よりも小さいシーケンス内のすべてのデータをこの番号に変更します。
q演算後にシーケンスを指定する必要があります。
分析:
線分ツリーは単一点の変更と間隔の更新をサポートしているため、この問題は線分ツリーで解決できます。
各間隔は間隔の最小値を維持し、遅延タグタグは変更される間隔の最小値(つまり、操作2で指定された値)を記録します。サブインターバルの最小値がタグより大きい場合、プッシュダウンする必要はありません(インターバルを更新する必要はありません)。それ以外の場合は、プッシュダウンして更新します。
操作1は、通常の線分ツリーの1点変更です。
各リーフノードの値の最後のクエリが答えです。ここでは、各遅延マークがリーフノードにプッシュされることを確認するためにクエリを実行する必要があります。
具体的な説明については、コードを参照してください。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <cstdio>
#include <map>
#include <iomanip>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
ll a[200005];
struct segtree{
int l,r;
ll minn;
int tag;//延迟标记
};
segtree t[200005*4];
void pushup(int p){//上推,维护区间最小值
int lson=p*2,rson=p*2+1;
t[p].minn=min(t[lson].minn,t[rson].minn);
}
void pushdown(int p){//将父节点的状态向下传递(延迟标记的传递)
if(t[p].tag!=-1){//说明父节点的状态已被改变
int lson=p*2,rson=p*2+1;
//子节点的tag小于父节点或者子节点未被更新,需要下推,另外下推后要用子节点的tag更新子节点区间的最小值
if(t[lson].tag<t[p].tag||t[lson].tag==-1) t[lson].tag=t[p].tag;
if(t[lson].minn<t[lson].tag) t[lson].minn=t[lson].tag;
if(t[rson].tag<t[p].tag||t[rson].tag==-1) t[rson].tag=t[p].tag;
if(t[rson].minn<t[rson].tag) t[rson].minn=t[rson].tag;
t[p].tag=-1;//父节点传递完成后标志复原
}
}
void build(int p,int l,int r){//建树
int mid=(l+r)/2;
t[p].l=l;
t[p].r=r;
t[p].tag=-1;
if(l==r){
t[p].minn=a[l];
return;
}
build(p*2,l,mid);
build(2*p+1,mid+1,r);
pushup(p);
}
//查询叶子节点的值
ll query(int index,int p){
if(t[p].l==t[p].r){
return t[p].minn;
}
pushdown(p);//首先将本节点的状态传递下去,便于接下来的递归查询
int lson=p*2;
int rson=p*2+1;
int mid=(t[p].l+t[p].r)/2;
if(index<=mid) return query(index,lson);
else return query(index,rson);
}
//单点修改
void change(int p,int index,ll num){
if(t[p].l==t[p].r){
t[p].minn=num;
t[p].tag=-1;
return;
}
int mid=(t[p].l+t[p].r)/2;
pushdown(p);//修改前要先下推
if(index<=mid){
change(p*2,index,num);
}
else{
change(p*2+1,index,num);
}
pushup(p);//修改完成后要上推
}
//区间更新
void update(int p,int L,int R,ll x){
int lson=p*2,rson=p*2+1;
if(L<=t[p].l&&R>=t[p].r){//区间被完全覆盖
if(t[p].minn<x){
t[p].tag=x;//这里区间的tag被更新为要求的值
t[p].minn=x;
}
return;//由于此处直接返回,所以需要延迟标记,此处未更新该节点的子树
}
pushdown(p);//进入子节点前先传递延迟标记,便于接下来的递归更新
int mid=(t[p].l+t[p].r)/2;
if(L<=mid){
update(lson,L,R,x);
}
if(R>mid){
update(rson,L,R,x);
}
pushup(p);//子节点更新返回后要更新父节点
}
int main(){
int n,q;
scanf("%d",&n);
rep(i,1,n){
scanf("%I64d",&a[i]);
}
build(1,1,n);
scanf("%d",&q);
rep(i,1,q){
int com;
scanf("%d",&com);
if(com==1){
int p;
ll x;
scanf("%d%I64d",&p,&x);
change(1,p,x);
}
else{
ll x;
scanf("%I64d",&x);
update(1,1,n,x);
}
}
rep(i,1,n){
printf("%I64d%c",query(i,1),(i==n?'\n':' '));
}
return 0;
}