题面传送门

神仙 DS。

首先关于第一问可以轻松想到一个 DP,\(dp_{i,j}\) 表示考虑到第 \(i\) 位,这一位奇偶性为 \(j\) 的最大权值,时间复杂度 \(n^2q\),可以拿到 \(3\) 分的好成绩(大雾),如果稍微动点脑子使用前缀和优化还可以拿到 \(9\) 分的好成绩,但是显然这个做法是没有前途的,因为这个 DP 对第二问没有任何启发作用,并且也无法将它优化到每次询问都可以在低于 \(\mathcal O(n)\) 内处理的复杂度,因此我们只好放弃这个思路。

按照我们做题的经验,我们考虑分析一下这个权值最大的序列有什么性质,手玩几组数据后你就能发现以下性质:

Observation 1. 对于一个序列 \(a\),我们如果将 \(a\) 划分成一段段上升段和下降段,那么我们选择的子序列的第 \(2k+1\) 个元素一定是第 \(k\) 个极小值点,第 \(2k+2\) 个元素一定是第 \(k\) 个极大值点

具体证明可用调整法,留给读者自己思考。

也就是说,第 \(2k+1\) 和第 \(2k+2\) 个元素一定是第 \(k\) 个上升段的开头和结尾,这个序列的权值就是所有上升段的结尾值减去开头的值。

注意到这个上升段、下降段维护起来有点棘手,因此我们不妨换个角度,用与其联系紧密的差分序列 \(b_i=a_{i+1}-a_i\) 来看待这个问题,因为差分序列的正负性即暗示了该元素位于上升段中还是下降段中。注意到对于一段上升序列,其末尾元素与开头元素的差就是这个差分序列中所有元素的和,而对于下降序列,其差分序列中所有元素都非正,因此我们稍微转化一下即可得到以下性质:

Observation 2. 对于序列 \(a\),其子序列点值的最大值就是其差分序列中所有正数的和

这样第一问就解决了,因为每次区间加操作最多影响差分序列中两个元素的权值,因此这个贡献是可以 \(\mathcal O(1)\) 计算的。

接下来考虑第二问,每次操作相当于移动最优序列中下标 \(2k,2k+1\) 元素,而根据之前的推论这两个元素必然是同一个下降段的首尾元素,而显然对于下降段中的某个元素 \(x\),将 \(x\) 移至 \(x+1\) 会使得权值加上 \(b_x\),因此操作等价于选择某个下降段中的某个不相交的前缀和后缀,并令权值加上这段前缀和后缀中所有元素的和,问最少几次操作能使权值 \(\le 0\)。

由于下降段的差分序列中每个元素都 \(\le 0\),因此我们肯定会贪心地选择长度加起来等于下降段减 \(1\) 的前后缀,也就是将下标 \(2k,2k+1\) 元素移至某对相邻位置 \(p,p+1\),这样贡献就是这个相邻段中差分序列权值之和加上 \(b_p\),而我们显然会选择 \(b_p\) 最大的 \(p\) 作为目标位置,因此可以得到:

Observation 3. 对于差分序列上每个非正段,其移动一次权值最大减少量就是这段差分序列权值之和减去这段中 \(b_i\) 的最大值。

因此对于每个非正整数的段,其进行一次操作权值的最大减少量是确定的,而我们肯定会优先选择减少量最大的下降段,因此我们可以将所有连续段权值最大减少量扔进一个平衡树,每次询问在平衡树上二分即可。每次修改操作导致连续段的变化量是 \(\mathcal O(1)\) 级别的,用个 set 维护一下即可,差分数组的区间最小值可用线段树维护。于是这题就被我们拆分成几步解决了,时间复杂度 \(n\log n\)。

注意点:注意特殊判断第一段和最后一段下降段。

码了 188 行

const int MAXN=1e5;
int n,qu,a[MAXN+5];ll b[MAXN+5],sum=0;
struct segtree{
struct node{int l,r;ll sum,mx;} s[MAXN*4+5];
void clear(int k){
if(s[k].l==s[k].r) return s[k].l=s[k].r=s[k].sum=s[k].mx=0,void();
clear(k<<1);clear(k<<1|1);s[k].l=s[k].r=s[k].sum=s[k].mx=0;
}
void pushup(int k){
s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);
s[k].sum=s[k<<1].sum+s[k<<1|1].sum;
}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return s[k].mx=s[k].sum=b[l],void();
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
void modify(int k,int p,ll x){
if(s[k].l==s[k].r) return s[k].mx=s[k].sum=x,void();int mid=s[k].l+s[k].r>>1;
(p<=mid)?modify(k<<1,p,x):modify(k<<1|1,p,x);pushup(k);
}
ll getmax(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].mx;int mid=s[k].l+s[k].r>>1;
if(r<=mid) return getmax(k<<1,l,r);else if(l>mid) return getmax(k<<1|1,l,r);
else return max(getmax(k<<1,l,mid),getmax(k<<1|1,mid+1,r));
}
ll getsum(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].sum;int mid=s[k].l+s[k].r>>1;
if(r<=mid) return getsum(k<<1,l,r);else if(l>mid) return getsum(k<<1|1,l,r);
else return getsum(k<<1,l,mid)+getsum(k<<1|1,mid+1,r);
}
} st;
struct fhqtreap{
struct node{int ch[2],key,siz;ll sum,val;} s[MAXN*3+5];
int rt,ncnt;
fhqtreap(){rt=ncnt=0;}
void init(){
for(int i=1;i<=ncnt;i++) s[i].ch[0]=s[i].ch[1]=s[i].key=s[i].siz=s[i].sum=s[i].val=0;
rt=ncnt=0;
}
void pushup(int k){
s[k].sum=s[s[k].ch[0]].sum+s[s[k].ch[1]].sum+s[k].val;
s[k].siz=s[s[k].ch[0]].siz+s[s[k].ch[1]].siz+1;
}
void split(int k,ll v,int &a,int &b){
if(!k) return a=b=0,void();
if(s[k].val<=v) return a=k,split(s[k].ch[1],v,s[k].ch[1],b),pushup(k),void();
else return b=k,split(s[k].ch[0],v,a,s[k].ch[0]),pushup(k),void();
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(s[x].key<s[y].key) return s[x].ch[1]=merge(s[x].ch[1],y),pushup(x),x;
else return s[y].ch[0]=merge(x,s[y].ch[0]),pushup(y),y;
}
void insert(ll x){
// printf("insert %lld\n",x);
s[++ncnt].sum=x;s[ncnt].val=x;s[ncnt].key=rand();s[ncnt].siz=1;
int k1,k2;split(rt,x-1,k1,k2);rt=merge(merge(k1,ncnt),k2);
// printf("%d\n",rt);
}
void del(ll x){
// printf("del %lld\n",x);
int k1,k2,k3;split(rt,x-1,k1,k2);split(k2,x,k2,k3);
rt=merge(merge(k1,s[k2].ch[0]),merge(s[k2].ch[1],k3));
}
int walk(int k,ll v){
if(v<=0) return 0;
if(s[k].sum+v>0) return -1;
// printf("%d %lld %lld %lld %lld\n",k,v,s[s[k].ch[0]].sum,s[s[k].ch[1]].sum);
if(s[s[k].ch[0]].sum+v<=0) return walk(s[k].ch[0],v);
else if(s[s[k].ch[0]].sum+s[k].val+v<=0) return s[s[k].ch[0]].siz+1;
else return s[s[k].ch[0]].siz+1+walk(s[k].ch[1],v+(s[s[k].ch[0]].sum+s[k].val));
}
} trp;
ll getval(int l,int r){return st.getsum(1,l,r)-st.getmax(1,l,r);}
set<pii> itv;
void delitv(int l,int r){
assert(l<=r);
// printf("delitv %d %d\n",l,r);
itv.erase(itv.find(mp(l,r)));
if((l^1)&&(r^(n-1))) trp.del(getval(l,r));
}
void insitv(int l,int r){
assert(l<=r);
// printf("insitv %d %d\n",l,r);
itv.insert(mp(l,r));
if((l^1)&&(r^(n-1))) trp.insert(getval(l,r));
}
void chg(int x,ll v){
sum-=max(b[x],0ll);sum+=max(v,0ll);
if(b[x]>0&&v<=0){
pii nxt=*itv.upper_bound(mp(x,n+1));
pii pre=*--itv.lower_bound(mp(x,0));
int l=x,r=x;
if(nxt.fi==x+1) delitv(nxt.fi,nxt.se),r=nxt.se;
if(pre.se==x-1) delitv(pre.fi,pre.se),l=pre.fi;
st.modify(1,x,v);insitv(l,r);
} else if(b[x]<=0&&v>0){
pii lrg=*--itv.lower_bound(mp(x,n+1));
delitv(lrg.fi,lrg.se);st.modify(1,x,v);
if(lrg.se^x) insitv(x+1,lrg.se);
if(lrg.fi^x) insitv(lrg.fi,x-1);
} else if(b[x]<=0){
pii t=*--itv.lower_bound(mp(x,n+1));
delitv(t.fi,t.se);st.modify(1,x,v);
insitv(t.fi,t.se);
} else st.modify(1,x,v);
b[x]=v;
}
void clear(){
memset(a,0,sizeof(a));memset(b,0,sizeof(b));
sum=0;itv.clear();trp.init();st.clear(1);
}
void solve(){
scanf("%d%d",&n,&qu);clear();
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++) b[i]=a[i+1]-a[i],sum+=max(b[i],0ll);
st.build(1,1,n-1);itv.insert(mp(-1,-1));itv.insert(mp(n+1,n+1));
int pre=0;
for(int i=1;i<n;i++) if(b[i]>0){
if(pre^(i-1)) insitv(pre+1,i-1);
pre=i;
} if(pre^(n-1)) insitv(pre+1,n-1);
while(qu--){
int opt;scanf("%d",&opt);
if(opt==0){
int l,r,v;scanf("%d%d%d",&l,&r,&v);
if(l^1) chg(l-1,b[l-1]+v);
if(r^n) chg(r,b[r]-v);
} else {
printf("%lld %d\n",sum,trp.walk(trp.rt,sum));
}
}
}
int main(){int qu;scanf("%d",&qu);while(qu--) solve();return 0;}

洛谷 P4497 - [WC2011]拼点游戏(数据结构综合)的更多相关文章

  1. P4497 [WC2011]拼点游戏

    P4497 [WC2011]拼点游戏 在我的 cnblogs 中查看 数据结构大杂烩 + 阿巴细节题. 调了三个小时. 首先考虑第一小问的答案. 注意到点数的计算方式是先负后正的形式,不妨看做选出 \ ...

  2. 【洛谷p1012】拼数

    (今天yuezhuren大课间放我们出来了……) (另外今天回了两趟初中部) 拼数[传送门] 洛谷算法标签: (然鹅这两个学的都不好,能过真的how strange) 开始的时候没读题啊,直接暴力so ...

  3. 洛谷 P2059 [JLOI2013]卡牌游戏(概率dp)

    题面 洛谷 题解 \(f[i][j]\)表示有i个人参与游戏,从庄家(即1)数j个人获胜的概率是多少 \(f[1][1] = 1\) 这样就可以不用讨论淘汰了哪些人和顺序 枚举选庄家选那张牌, 枚举下 ...

  4. 洛谷 大牛分站 P1000 超级玛丽游戏

    题目背景 本题是洛谷的试机题目,可以帮助了解洛谷的使用. 建议完成本题目后继续尝试P1001.P1008. 题目描述 超级玛丽是一个非常经典的游戏.请你用字符画的形式输出超级玛丽中的一个场景. *** ...

  5. BZOJ3191或洛谷2059 [JLOI2013]卡牌游戏

    BZOJ原题链接 洛谷原题链接 我们可以倒着来\(DP\). 设\(f[i][j]\)表示剩余\(i\)个人,从庄家数起第\(j\)个人的胜率,设当前枚举到第\(k\)张牌,该情况下这一轮淘汰的位置为 ...

  6. 洛谷 P4819 [中山市选]杀人游戏

    洛谷 题目就是让我们在DAG中找到一些点,覆盖所有点. 因为是DAG,可以想到tarjan缩一下点.假设我们需要找x个点,那么答案就是(n-x)/n. 我们怎么选点呢? 敏锐的我们很快就能想到,直接选 ...

  7. 【洛谷】P1247取火柴游戏

    题目链接:https://www.luogu.org/problemnew/show/P1247 题意:nim取石子的题意,多了一个判断先手赢的话,输出先手第一把怎么拿,以及拿完之后每堆还剩多少. 题 ...

  8. 【洛谷4424】[HNOI_AHOI2018]寻宝游戏(我也不知道括号里该写啥)

    题目 洛谷 4424 分析 感觉思路比较神仙. 对于按位与和按位或两种运算,显然每一位是独立的,可以分开考虑. 对于某一位,「与 \(0\)」会将这一位变成 \(0\),「或 \(1\)」会将这一位变 ...

  9. 洛谷 P3781 - [SDOI2017]切树游戏(动态 DP+FWT)

    洛谷题面传送门 SDOI 2017 R2 D1 T3,nb tea %%% 讲个笑话,最近我在学动态 dp,wjz 在学 FWT,而我们刚好在同一天做到了这道题,而这道题刚好又是 FWT+动态 dp ...

随机推荐

  1. Hadoop面试题(四)——YARN

    1.简述hadoop1与hadoop2 的架构异同 1)加入了yarn解决了资源调度的问题. 2)加入了对zookeeper的支持实现比较可靠的高可用. 2.为什么会产生 yarn,它解决了什么问题, ...

  2. Java继承、重写与重载

    1.java继承 1.1概念 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为. 继承可以使用extends和implem ...

  3. 2020BUAA软工个人博客作业

    2020BUAA软工个人博客作业 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业 我在这个课程的目标是 学 ...

  4. Kotlin/Native 用KMM写Flutter插件

    一.用KMM写Flutter插件 Google官方有一个写Flutter例子How to write a Flutter plugin,这里把Google plugin_codelab 例子改成用KM ...

  5. USART波特率 vs SPI速率--学习笔记

    本篇文章将与大家探讨USART波特率 vs SPI速率.这里提出一个问题,为什么USART的波特率是内核时钟的1/8或者1/16,而SPI最快的频率可以是内核时钟的1/2. 请大家带着这个问题来阅读本 ...

  6. 【做题记录】[NOI2008] 假面舞会—有向图上的环与最长链

    luogu 1477 [NOI2008] 假面舞会 容易发现: 如果图中没有环,那么面具种数一定是所有联通块内最长链之和,最少为 \(3\) . 如果有环,则面具种数一定是所有环的大小的最大公约数. ...

  7. Android WebView 实现文件选择、拍照、录制视频、录音

    原文地址:Android WebView 实现文件选择.拍照.录制视频.录音 | Stars-One的杂货小窝 Android中的WebView如果不进行相应的设置,H5页面的上传按钮是无法触发And ...

  8. 关于docker中容器可以Ping通外网,真机无法Ping通容器的问题

    首先我们要知道整体的框架结构,docker是我们安装在centos7上的,而centos7是安装在vmware上.其中docker中还有若干容器运行. 整体框架图如下: 我们将它分为两部分,一部分是d ...

  9. KMP算法-字符匹配

    字符匹配模式-KMP算法 j直接跳到了2的位置,因为在之前的都相同. 那么就需要求如果不等了之后,j需要回跳的位置next[j] 如果tk'与tj相等,则next [j+1]=k'+1 如果tk'与t ...

  10. IDEA插件和个性化配置推荐

    插件推荐 我自己现在使用的一些插件和一些自己感觉比较舒服配置分析给大家 idea如何安装插件: 如果打开设置没有看到,直接搜索plugins 然后在这里搜索即可 CodeGlance 小地图 和vsc ...