题面传送门

神仙 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. HCIP-RSTP

    端口角色 根端口(RP): 非根桥收到最优的BPDU配置信息的端口为根端口,(到根桥开销最小的端口),根桥没有根端口. 指定端口(DP): 两非根交换机之间连接的两个端口必有一个为指定端口,比较两个非 ...

  2. 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色

    总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...

  3. Noip模拟78 2021.10.16

    这次时间分配还是非常合理的,但可惜的是$T4$没开$\textit{long long}$挂了$20$ 但是$Arbiter$上赏了蒟蒻$20$分,就非常不错~~~ T1 F 直接拿暴力水就可以过,数 ...

  4. 21.6.17 test

    \(NOI\) 模拟赛. \(T1\) 正解树形DP,由于不是很熟悉概率和期望所以打了个20pts暴力,说不定见多了概率能打出60pts半正解?最后的虚树更不会. \(T2\) 又是概率,还有坐标数量 ...

  5. (继承)Program2.1

    覆盖和重写的意思是一样?结果是一样的 例如: 1 class Parent: # 定义父类 2 def myMethod(self): 3 print('调用父类方法') 4 5 6 class Ch ...

  6. 认识Linux系统中的inode,硬链接和软链接

    在学习和创建软链接遇到了一点问题,总结一下: 在当前文件夹下面建立了两个临时文件夹tempdir1和tempdir2,然后在tempdir2里面创建了一个hello文件,然后用指令ln -s temp ...

  7. minimum-depth-of-binary-tree leetcode C++

    Given a binary tree, find its minimum depth.The minimum depth is the number of nodes along the short ...

  8. Luogu P1297 [国家集训队]单选错位 | 概率与期望

    题目链接 题解: 单独考虑每一道题目对答案的贡献. 设$g_i$表示gx在第$i$道题目的答案是否正确(1表示正确,0表示不正确),则$P(g_i=1)$表示gx在第$i$道题目的答案正确的概率. 我 ...

  9. lamp 架构的理解

    1,lamp架构下的求情过程如下: 2,httpd服务器连接php服务器的三种方式 3,php和mysql的连接

  10. Ubuntu16.04安装apache hive

    0.常规配置操作可参照以下网址: 0.1 Ubuntu安装hive,并配置mysql作为元数据库http://dblab.xmu.edu.cn/blog/install-hive/ ------以下为 ...