洛谷 P4497 - [WC2011]拼点游戏(数据结构综合)
神仙 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]拼点游戏(数据结构综合)的更多相关文章
- P4497 [WC2011]拼点游戏
P4497 [WC2011]拼点游戏 在我的 cnblogs 中查看 数据结构大杂烩 + 阿巴细节题. 调了三个小时. 首先考虑第一小问的答案. 注意到点数的计算方式是先负后正的形式,不妨看做选出 \ ...
- 【洛谷p1012】拼数
(今天yuezhuren大课间放我们出来了……) (另外今天回了两趟初中部) 拼数[传送门] 洛谷算法标签: (然鹅这两个学的都不好,能过真的how strange) 开始的时候没读题啊,直接暴力so ...
- 洛谷 P2059 [JLOI2013]卡牌游戏(概率dp)
题面 洛谷 题解 \(f[i][j]\)表示有i个人参与游戏,从庄家(即1)数j个人获胜的概率是多少 \(f[1][1] = 1\) 这样就可以不用讨论淘汰了哪些人和顺序 枚举选庄家选那张牌, 枚举下 ...
- 洛谷 大牛分站 P1000 超级玛丽游戏
题目背景 本题是洛谷的试机题目,可以帮助了解洛谷的使用. 建议完成本题目后继续尝试P1001.P1008. 题目描述 超级玛丽是一个非常经典的游戏.请你用字符画的形式输出超级玛丽中的一个场景. *** ...
- BZOJ3191或洛谷2059 [JLOI2013]卡牌游戏
BZOJ原题链接 洛谷原题链接 我们可以倒着来\(DP\). 设\(f[i][j]\)表示剩余\(i\)个人,从庄家数起第\(j\)个人的胜率,设当前枚举到第\(k\)张牌,该情况下这一轮淘汰的位置为 ...
- 洛谷 P4819 [中山市选]杀人游戏
洛谷 题目就是让我们在DAG中找到一些点,覆盖所有点. 因为是DAG,可以想到tarjan缩一下点.假设我们需要找x个点,那么答案就是(n-x)/n. 我们怎么选点呢? 敏锐的我们很快就能想到,直接选 ...
- 【洛谷】P1247取火柴游戏
题目链接:https://www.luogu.org/problemnew/show/P1247 题意:nim取石子的题意,多了一个判断先手赢的话,输出先手第一把怎么拿,以及拿完之后每堆还剩多少. 题 ...
- 【洛谷4424】[HNOI_AHOI2018]寻宝游戏(我也不知道括号里该写啥)
题目 洛谷 4424 分析 感觉思路比较神仙. 对于按位与和按位或两种运算,显然每一位是独立的,可以分开考虑. 对于某一位,「与 \(0\)」会将这一位变成 \(0\),「或 \(1\)」会将这一位变 ...
- 洛谷 P3781 - [SDOI2017]切树游戏(动态 DP+FWT)
洛谷题面传送门 SDOI 2017 R2 D1 T3,nb tea %%% 讲个笑话,最近我在学动态 dp,wjz 在学 FWT,而我们刚好在同一天做到了这道题,而这道题刚好又是 FWT+动态 dp ...
随机推荐
- 敏捷 Scrum Master 的難點
什麼是 Scrum Master? Scrum master 是一個團隊角色,負責確保團隊遵守敏捷方法和原則並符合團隊的流程和實踐. Scrum Master 促進敏捷開發團隊成員之間的協作.Scru ...
- 第4次 Beta Scrum Meeting
本次会议为Beta阶段第4次Scrum Meeting会议 会议概要 会议时间:2021年6月4日 会议地点:「腾讯会议」线上进行 会议时长:0.5小时 会议内容简介:对完成工作进行阶段性汇报:对下一 ...
- UltraSoft - Alpha - Scrum Meeting 4
Date: Apr 18th, 2020. 会议内容为 例行汇报. Scrum 情况汇报 进度情况 组员 负责 前两日进度 后两日任务 CookieLau PM 完成前后端交互规格的约定,了解前后端进 ...
- 单片机STM32的启动文件详解--学习笔记
启动文件简介 启动文件由汇编编写,是系统上电复位后第一个执行的程序.主要做了以下工作: 1.初始化堆栈指针SP=_initial_sp 2.初始化PC 指针=Reset_Handler 3.初始化中断 ...
- HITS算法简介
1.算法名称 超文本敏感标题搜索 (Hyperlink-Induced Topic Search) 2.算法背景 HITS 算法是由康奈尔大学的Jon Kleinberg 博士于1997 年首先提出的 ...
- 攻防世界 杂项 12.Training-Stegano-1
题目描述: 这是我能想到的最基础的图片隐写术.啊这 题目分析: 最初还以为直接右击属性查看呢 然后用notepad++看看,一团乱码,结果在最后发现了passwd, 然后这就是flag:stegano ...
- 第10课 OpenGL 3D世界
加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游.这一课使用第一课的代码,当然在课程说明中我只介绍改变了代码. 这一课是由Lionel Brits (βtelge ...
- Robot Framework操作MySQL数据库
1.安装databaselibrary.pymysql 通过cmd命令执行:pip install robotframework-databaselibrary cmd命令执行:pip install ...
- webpack 打包图片资源
webpack 打包图片资源 /** * loader: 1. 下载 2. 使用(配置) * plugins:1. 下载 2. 引入 3.使用 */ // 用来拼接绝对路径的方法 const {res ...
- MyBatis-Plus 快速入门
1.简介 MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发.提高效率而生. 1.1.特性 无侵入:只做增强不做改变, ...