[DS记录]P5609 [Ynoi2013] 对数据结构的爱

command_block

2021-07-05 13:28:16

Personal

**题意** : 给定常数 $p$ ,定义函数 : $$ {\rm add}(x,y)= \begin{cases} x+y-p&(x+y\geq p)\\ x+y&\rm otherwise \end{cases} $$ 给出一个长为 $n$ 的序列 $A$。 定义 $S(l,r)$ : $$ S(l,r)= \begin{cases} A_l&(l=r)\\ {\rm add}\big(S(l,r-1),A_r\big)&\rm otherwise \end{cases} $$ 即用 $\rm add$ 从左往右“求和”。 $m$ 次查询 $S(l,r)$ 的值。 $n\leq 10^6,m\leq 2\times 10^5$ ,时限$\texttt{2s}$ ,空限 $\texttt{512M}$。 ------------ 好题。 $n$ 很大,考虑 ${\rm polylog}$ 数据结构。 我们只需求出 “减 $p$” 发生的次数 $t$ ,答案即为 $\sum A_{l\sim r}-tp$。 - **观察** : 初始值越大,后面进行同样的加法,所触发的减法次数越多。 使用线段树维护。 对于每个节点,记 $t_i$ 表示初始值为 $i$ 时出发的减法次数。 记 $c_i$ 表示触发 $i$ 次减法所需的最小初始值,这里有 $i\leq r-l+1$。 然后 $t$ 可以由 $c$ 描述,其中 $t_{[c_i,c_{i+1})}=i$。 考虑如何合并 $L,R$ 两个节点。 $$U.t_i=L.t_i+R.t_{L.s-p\times L.t_i}$$ 用上式可以快速求 $t$。利用单调性可以二分求解 $c$ ,但效率低下,难以通过。 注意到上式的 $i$ 只需考虑 $c$ 中的值,可以写出由 $c$ 到 $c$ 的转移: $$U.c_{x+y}=\min_{x,y}\max\big(L.c_x,R.c_y-(L.s-xp)\big)$$ 对 $\max$ 分类讨论。 考虑 $\max$ 内的判据 $R.c_y-(L.s-xp)\leq L.c_x$ (贡献为左侧 $L.c.x$),移项可得 $R.c_y\leq L.c_x+L.s-xp$ ,可以证明右边是单调不降的。 - **说明** : 即证 $c_x-c_{x-1}\geq p$ 。这容易理解 : 若想要减法多触发一次,至少将初值增大 $p$。 这可以说明,随着 $x$ 的增大,满足条件的 $y$ 的前缀单调不缩。这造就了如下的分界线 : ![](https://cdn.luogu.com.cn/upload/image_hosting/g75g8eqz.png) 如图,蓝色部分是贡献为左的,红色部分是贡献为右的。 由于 $c_x$ 单调增加,蓝色部分的贡献值越靠左贡献值越小,(结合贡献直线的形式)只需考虑分界上的方案。 于是只需线性扫描即可。建树的时空复杂度均为 $O(n\log n)$。 查询时,需要支持给定一个初值求出通过区间后的值。直接在 $c$ 数组中二分即可,复杂度 $O(m\log^2n)$。 代码很简短: ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 1050000 using namespace std; struct Node{int len;ll *c,s;}a[MaxN<<1]; ll _buf[MaxN*22],*tp=_buf; int p; void up(int u) { int l=u<<1,r=u<<1|1; a[u].s=a[l].s+a[r].s; for (int i=0;i<=a[u].len;i++)a[u].c[i]=1ll<<60; #define chk(x,y) a[u].c[x+y]=min(a[u].c[x+y],max(a[l].c[x],a[r].c[y]-(a[l].s-1ll*x*p))); int x=0,y=0; for (;x<=a[l].len;x++){ chk(x,y);if (x>0&&y<a[r].len)chk((x-1),y+1); while(y<a[r].len&&a[r].c[y+1]<=a[l].c[x]+a[l].s-1ll*x*p) {y++;chk(x,y);if (x>0&&y<a[r].len)chk((x-1),y+1);} }while(y<a[r].len){y++;chk(a[l].len,y);} } int x[MaxN]; void build(int l,int r,int u) { a[u].len=r-l+1; a[u].c=tp;tp+=a[u].len+1; if (l==r){ a[u].s=x[l]; a[u].c[0]=-1ll<<60; a[u].c[1]=p-x[l]; return ; }int mid=(l+r)>>1; build(l,mid,u<<1); build(mid+1,r,u<<1|1); up(u); } int wfl,wfr;ll ret; void qry(int l,int r,int u) { if (wfl<=l&&r<=wfr){ int t=upper_bound(a[u].c,a[u].c+a[u].len+1,ret)-a[u].c-1; ret+=a[u].s-1ll*t*p; return ; }int mid=(l+r)>>1; if (wfl<=mid)qry(l,mid,u<<1); if (mid<wfr)qry(mid+1,r,u<<1|1); } int n,m; int main() { scanf("%d%d%d",&n,&m,&p); for (int i=1;i<=n;i++)scanf("%d",&x[i]); build(1,n,1); for (int i=1;i<=m;i++){ scanf("%d%d",&wfl,&wfr); ret=0;qry(1,n,1); printf("%lld\n",ret); }return 0; } ``` - CF 重题 : [CF1172F Nauuo and Bug](https://www.luogu.com.cn/problem/CF1172F)