[DS记录]P5069 [Ynoi2015] 纵使日薄西山

command_block

2021-07-02 16:42:59

Personal

**题意** : 对于序列 $A$ ,不断进行如下操作 : 选择 $A$ 中最靠左的最大值 $A_x$ ,将 $A_{x-1},A_x,A_{x+1}$ 都减去一,然后将小于 $0$ 的元素置为 $0$。 记 $S(A)$ 为:整个序列变为 $0$ 之前进行的操作数。 维护一个长为 $n$ 的序列 $A$ ,支持单点修改,查询 $S(A)$ 的值。 $n,q\leq 10^5$ ,时限$\texttt{1s}$。 ------------ 先不考虑修改,思考如何求出 $S(A)$。 观察 : 若我们操作了 $A_{x-1},A_x,A_{x+1}$ ,之后总有 $A_x>A_{x-1},A_x\geq A_{x+1}$ ,故不会再以 $x-1,x+1$ 为中心进行操作。 其他可能的操作均不再会影响 $A_x$ 的值,故 $A_x$ 必须要使用 $A_x$ 次操作。 不妨将过程等价地改为 : 每次选出最靠左的最大值 $A_x$,将 $A_{x-1},A_x,A_{x+1}$ 变为 $0$ ,次数加上 $A_x$。 于是答案就是所有被操作的位置的和。 进一步观察,对于单调不增的序列,被操作的下标是所有奇数。 对于一般的序列,我们将其看成若干段单调序列。 对于“山峰”,必然有贡献。 对于“山坡”上的点,下标奇偶性与对应“山峰”相同的位置有贡献。 对于“山谷”,必须奇偶性与相邻的两个山峰都相同才有贡献。 用树状数组分别维护奇下标与偶下标的部分和。用 `std::set` 维护山峰和山谷。 复杂度 $O(n\log n)$。 本题对时间常数的要求较为宽松,可以适当简化代码。 ```cpp #include<algorithm> #include<cstdio> #include<set> #define ll long long #define MaxN 100500 using namespace std; int n; #define lbit(p) (p&-p) struct SumDS { ll t[MaxN]; void add(int p,int c) {while(p<=n){t[p]+=c;p+=lbit(p);}} ll qry(int p){ ll ret=0; while(p){ret+=t[p];p^=lbit(p);} return ret; } }T[2]; set<int> s;ll ans; int a[MaxN],fl[MaxN]; int chk(int p) { if (fl[p]==1)return a[p]; if (p<1||p>n||fl[p]!=2)return 0; int pre=*(--s.lower_bound(p)) ,nxt=*s.upper_bound(p); if (fl[pre]==1&&(p&1)!=(pre&1))return 0; if (fl[nxt]==1&&(p&1)!=(nxt&1))return 0; return a[p]; } ll calc(int l,int r) { int sav=-1; if (fl[l]==1&&fl[r]==2)sav=l&1; if (fl[l]==2&&fl[r]==1)sav=r&1; return sav==-1 ? 0 : T[sav].qry(r-1)-T[sav].qry(l); } int calc2(int l,int p,int r) { if (fl[l]==1&&fl[r]==2)return (l&1)==(p&1) ? a[p] : 0; if (fl[l]==2&&fl[r]==1)return (r&1)==(p&1) ? a[p] : 0; return 0; } void add(int p) { int pre=*(--s.lower_bound(p)) ,nxt=*s.upper_bound(p); if (fl[p]){ ans-=calc(pre,nxt)+chk(pre)+chk(nxt); s.insert(p); ans+=calc(pre,p)+calc(p,nxt)+chk(p)+chk(pre)+chk(nxt); }else { T[p&1].add(p,a[p]); ans+=calc2(pre,p,nxt); } } void del(int p) { int pre=*(--s.lower_bound(p)) ,nxt=*s.upper_bound(p); if (fl[p]){ ans-=calc(pre,p)+calc(p,nxt)+chk(p)+chk(pre)+chk(nxt); s.erase(p); ans+=calc(pre,nxt)+chk(pre)+chk(nxt); }else { T[p&1].add(p,-a[p]); ans-=calc2(pre,p,nxt); } } int gfl(int p) { if (a[p]>a[p-1]&&a[p]>=a[p+1])return 1; if (a[p]<=a[p-1]&&a[p]<a[p+1])return 2; return 0; } int m; int main() { scanf("%d",&n); for (int i=1;i<=n;i++)scanf("%d",&a[i]); fl[0]=fl[n+1]=2; s.insert(0);s.insert(n+1); for (int i=1;i<=n;i++) {fl[i]=gfl(i);add(i);} scanf("%d",&m); for (int i=1,p,x;i<=m;i++){ scanf("%d%d",&p,&x); if (1<=p-1)del(p-1); del(p); if (p+1<=n)del(p+1); a[p]=x; if (1<=p-1){fl[p-1]=gfl(p-1);add(p-1);} fl[p]=gfl(p);add(p); if (p+1<=n){fl[p+1]=gfl(p+1);add(p+1);} printf("%lld\n",ans); }return 0; } ```