题解:P11368 [Ynoi2024] After god

· · 题解

P11368 [Ynoi2024] After god 题解

前言

这是道好题。前置知识点: 线段树3

题意

对一个初始状态全为 0 的序列维护一个前缀最大值,有 n 次修改,每次修改完查询一个区间历史和。

解题思路

这个是题目给出的样例,从下往上第 i 行对应进行第 i 次操作后的 a 序列(如红框代表第 1 次修改完后的 a 序列)。其中标红数字的是每次的修改。

上面这幅图从下往上第 i 行对应进行第 i 次操作后的前缀最大值序列。有颜色部分代表 a 序列,其中蓝色代表每次修改。

我们发现每次修改,只会影响发生修改的位置及其之后的位置。(红框内为修改后影响前缀最大值的范围)

我们现在横过来(横过来处理的原因之后会说明),对序列轴进行扫描。 将修改优先按 x 从小到大排序,再按 t(修改的时间,即这是第几次修改)从大到小排序。然后用线段树进行区间 max 操作维护。最后询问区间历史和(历史和是什么与如何维护后面会讲)。

以第一行为例。

先处理 t 最大的这个 2,这里我们记第 i 行上一次处理的位置为 last_i(初始都为 m+1),那么我们对区间 [t,last_i-1](即 [6,6])进行区间 max 操作,然后 last_1 变为 6

再处理 4,此时 t=4last_1=6,就对 [4,5] 区间进行区间 max 操作。 再把 last_1 修改为 4

然后以此类推处理其他修改。每一行处理完后对于这一行的第 j 个修改,记录答案为 [1,t_j] 的历史和。

为什么维护时间维

可能有人会有疑问:为什么我们不直接维护序列,而要横向维护时间轴,对序列维扫描线?

原因是这样的,当对一个位置 x_i 修改时,若其在此前不为零,则我们需先抹除其对后面的影响,再修改。而区间取 max 无法回退,就会导致错误。

举个例子。(其中标蓝为修改,标黑为前缀最大值)

从下向上看,我们发现在第 3 次修改时,无法单纯通过前一层得到。而横向扫描就不会出现这种问题。

区间历史和如何维护

记第 i 次操作后第 j 个位置上的值为 a_{i,j},那么经过 k 次操作后,第 j 个位置上的历史和可以表示为如下。

h_j=\sum_{i=1}^{k}a_{i,j}

而区间 [l,r] 的区间历史和可以表示为如下。

hist_{l,r}=\sum_{j=l}^{r}h_j=\sum_{j=l}^{r}\sum_{i=1}^{k}a_{i,j}

假设我们在 i 时刻,对长为 len 的区间加了 x_i,那么在 t 时刻查询时这次修改对这段区间历史和的贡献就为 (t-i+1)\times x_i\times len

展开后得到:

h_i=(t+1)\times x_i\times len+(x_i\times i)\times len $$ hist_t=\sum_{i=1}^k [(t+1)\times x_i\times len+(x_i\times i)\times len] $$ $$ hist_t=(t+1)\times \sum_{i=1}^k (x_i\times len)+ \sum_{i=1}^k (x_i\times i\times len) $$ 而查询时我们知道 $t$ 的值,于是我们线段树就维护 $x_i$ 和 $x_i\times i$ 的区间和就行了。 其实区间取 max 也可以看为区间加,原因就不细讲了。 ## 代码实现 注意:我的代码中 $hist\_sum$ 存的不是历史和,而是 $x_i\times i$。 ```cpp line-numbers #include<bits/stdc++.h> #define ll long long using namespace std; ll n,m; ll last[1000007],ans[1000007]; struct Node{ ll t,x,y; bool operator <(const Node b)const{ if(x!=b.x) return x>b.x;//x小在前 return t<b.t;//t大在前 } }; priority_queue<Node> pq; struct segment_tree_beats{ ll minn,se_minn; ll min_cnt; ll hist_sum,sum; ll min_tag,hist_add_tag; }tr[4000007]; inline ll ls(ll p){return p<<1;} inline ll rs(ll p){return p<<1|1;} void push_up(ll p){ tr[p].hist_sum=tr[ls(p)].hist_sum+tr[rs(p)].hist_sum; tr[p].sum=tr[ls(p)].sum+tr[rs(p)].sum; if(tr[ls(p)].minn==tr[rs(p)].minn){ tr[p].minn=tr[ls(p)].minn; tr[p].se_minn=min(tr[ls(p)].se_minn,tr[rs(p)].se_minn); tr[p].min_cnt=tr[ls(p)].min_cnt+tr[rs(p)].min_cnt; } else if(tr[ls(p)].minn<tr[rs(p)].minn){ tr[p].minn=tr[ls(p)].minn; tr[p].se_minn=min(tr[ls(p)].se_minn,tr[rs(p)].minn); tr[p].min_cnt=tr[ls(p)].min_cnt; } else{ tr[p].minn=tr[rs(p)].minn; tr[p].se_minn=min(tr[rs(p)].se_minn,tr[ls(p)].minn); tr[p].min_cnt=tr[rs(p)].min_cnt; } return; } void butr(ll p,ll l,ll r){ tr[p].hist_sum=0; tr[p].sum=0; tr[p].min_tag=0; tr[p].hist_add_tag=0; if(l==r){ tr[p].min_cnt=1; tr[p].minn=0; tr[p].se_minn=LLONG_MAX; return; } ll mid=(l+r)>>1; butr(ls(p),l,mid); butr(rs(p),mid+1,r); push_up(p); return; } void update_node(ll p,ll min_add,ll hist_add){ tr[p].hist_sum+=hist_add*tr[p].min_cnt; tr[p].sum+=min_add*tr[p].min_cnt; tr[p].minn+=min_add; tr[p].min_tag+=min_add; tr[p].hist_add_tag+=hist_add; } void push_down(ll p){ ll old_minn=min(tr[ls(p)].minn,tr[rs(p)].minn); if(old_minn==tr[ls(p)].minn){ update_node(ls(p),tr[p].min_tag,tr[p].hist_add_tag); } if(old_minn==tr[rs(p)].minn){ update_node(rs(p),tr[p].min_tag,tr[p].hist_add_tag); } tr[p].min_tag=0; tr[p].hist_add_tag=0; } void max_update(ll ul,ll ur,ll p,ll l,ll r,ll k,ll base){ if(k<=tr[p].minn) return; if(k<tr[p].se_minn&&ul<=l&&ur>=r){ update_node(p,k-tr[p].minn,(k-tr[p].minn)*base); return; } push_down(p); ll mid=(l+r)>>1; if(ul<=mid) max_update(ul,ur,ls(p),l,mid,k,base); if(ur>mid) max_update(ul,ur,rs(p),mid+1,r,k,base); push_up(p); return; } ll hist_qry(ll ql,ll qr,ll p,ll l,ll r){ if(ql<=l&&qr>=r){ return tr[p].hist_sum; } push_down(p); ll res=0,mid=(l+r)>>1; if(ql<=mid) res+=hist_qry(ql,qr,ls(p),l,mid); if(qr>mid) res+=hist_qry(ql,qr,rs(p),mid+1,r); return res; } ll sum_qry(ll ql,ll qr,ll p,ll l,ll r){ if(ql<=l&&qr>=r){ return tr[p].sum; } push_down(p); ll res=0,mid=(l+r)>>1; if(ql<=mid) res+=sum_qry(ql,qr,ls(p),l,mid); if(qr>mid) res+=sum_qry(ql,qr,rs(p),mid+1,r); return res; } ll min_qry(ll ql,ll qr,ll p,ll l,ll r){ if(ql<=l&&qr>=r){ return tr[p].minn; } push_down(p); ll minn=LLONG_MAX,mid=(l+r)>>1; if(ql<=mid) minn=min(minn,min_qry(ql,qr,ls(p),l,mid)); if(qr>mid) minn=min(minn,min_qry(ql,qr,rs(p),mid+1,r)); return minn; } void init(){ for(int i=1;i<=n;i++){ last[i]=m+1; } butr(1,1,m); } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>m; for(int i=1;i<=m;i++){ Node a; cin>>a.x>>a.y; a.t=i; pq.push(a); } init(); ll last_x=-1; stack<Node> stk; while(!pq.empty()){ Node v=pq.top(); pq.pop(); if(last_x!=v.x){ while(!stk.empty()){ Node u=stk.top(); stk.pop(); ans[u.t]=sum_qry(1,u.t,1,1,m)*(u.x+1)+hist_qry(1,u.t,1,1,m); } } max_update(v.t,last[v.x]-1,1,1,m,v.y,-v.x); last[v.x]=v.t; stk.push(v); last_x=v.x; } while(!stk.empty()){ Node u=stk.top(); stk.pop(); ans[u.t]=sum_qry(1,u.t,1,1,m)*(u.x+1)+hist_qry(1,u.t,1,1,m); } for(int i=1;i<=m;i++){ cout<<ans[i]<<'\n'; } return 0; } ```