P4497 [WC2011]拼点游戏

在我的 cnblogs 中查看

数据结构大杂烩 + 阿巴细节题。

调了三个小时。


首先考虑第一小问的答案。

注意到点数的计算方式是先负后正的形式,不妨看做选出 \(2k\) 个数 \(p_1,p_2,\cdots,p_{2k}\),从小到大排序后计算 \(\sum u_{p_{2i}-p_{2i-1}}\)。再根据题目的区间修改操作,不难想到使用差分

设 \(d_i=u_i-u_{i-1}\),舍去 \(d_1\) 后得到一个长度为 \(n-1\) 的序列 \(d_2,d_3,\cdots,d_n\)。不难发现,选择一段连续的 \(d\) 并求和就相当于选择两个端点对应的 \(u\) 求差,即 \(d_l+d_{l+1}+\cdots+d_r=u_r-u_{l-1}\)。可以证明任何一种 \(d\) 中的选数方式都对应了 \(u\) 中的一种选数方式,故题目转化为在 \(d\) 中选出若干不相邻子段,求选出的数的和的最大值。

因此,为了让得分最大,我们只选取所有大于 \(0\) 的 \(d\) 即可,即 \(\sum_{i=2}^n\max(0,d_i)\)。


不妨设最终选取的 \(d\) 中的数的区间为 \([l_1,r_1],[l_2,r_2],\cdots,[l_m,r_m]\)。考虑小 Y(不是小 W)的修改操作的本质:选取两个编号相邻(不是在 \(d\) 中相邻)的区间 \([a,b],[c,d]\ (a\leq b<c\leq d,b+1<c)\),将 \(b,c\) 分别换成任意 \(z_1,z_2\in [b,c]\) 满足 \(z_1+1<z_2\)。

为了让减小的得分最多且最终两区间端点不相邻,又因为 \((b,c)\) 中全是非正数,故留下最大的那一个数即可。得分的减少量即为区间 \((b,c)\) 的和减去它的最大值,即 \(\left(\sum_{i=b+1}^{c-1}d_i\right)-\left(\max_{i=b+1}^{c-1}d_i\right)\)。

例如 positive integer | -6 | -5 | -7 | -8 | positive integers,我们就可以将 \(b\gets b+1\),\(c\gets c-2\),此时得分减小 \(6+7+8=21\),是最优的。

如果我们选了 \(m\) 个这样的区间,那么它们之间就有 \(m-1\) 个空隙可以用来减小分数。对于这 \(m-1\) 个空隙,分别计算它的得分最大减少量并丢到平衡树 or 动态开点线段树上。每个节点记录落在该区间的数的个数以及它们的和,则最终可以通过线段树上二分(原理类似主席树求区间第 k 小)求出答案。

同时,对于操作 \(1\),我们通过差分将其转化为了两次单点修改,而单点修改对于选择的区间的影响是 \(\mathcal{O}(1)\) 的,于是我们可以暴力 \(\log n\) 重新计算被影响的区间的答案即可。因为我们只关心 “被选择区间” 之间形成的 “空隙区间” 的信息,前者只需求和,所以记录后者即可。具体地,用线段树维护区间最大值与区间和,并用 set 维护 “空隙区间” 的端点信息,方便修改。

时间复杂度为 \(Tn\log n\sim Tn\log w\),其中 \(w\) 为值域。

当然,说起来轻巧,实现却并不简单。有亿些细节需要注意:都是踩过的坑啊。

  • 最后查询答案,在线段树上跑到叶子结点时,返回的不是该叶子结点维护的数的个数,而应该是 “剩余需要减少的得分” 除以 “该节点所表示的得分减少值” 上取整。这个错误太经典了。

  • 区间端点为 \(2\)(代码中为 \(1\))“非正整数值区间” 尽管不在两个 “被选择区间” 的中间,但也需要维护,因为若区间内部有一个位置变为正数,则该区间会裂开来分裂成左边的端点为 \(2\) 的区间和右边的 “空隙区间”。端点为 \(n\) 的情况同理。

  • 老老实实更新第一问的答案!不要投机取巧,踏踏实实地减去原贡献,加上新贡献。

  • 若一个位置从非正整数变为了非正整数,这并不意味着不需要修改,因为包含该位置的区间的和与最大值都改变了,所以 “得分减少量最大值” 也改变了。

  • Testcase 6,7 可能会出现相邻两个数相等的情况,即出现 \(d_i=0\)。辣鸡出题人用脚造数据。思考一下这种情况 \(d_i\) 究竟要不要选上。

    实际上,如果要使第二问答案最小,是不应该选 \(0\) 的。考虑 \(5\) 个相邻的数 \(a,b,c,d,e\),其中 \(a,e>0\),\(b,d<0\),\(c=0\)。如果 \(c\) 不被选,那么 \(b,c,d\) 为一个空隙区间,得分减少量为 \(|b+d|\)。但如果 \(c\) 被选,那么尽管不影响第一问答案,但会导致空隙区间分裂为 \(b\) 和 \(d\) 两个,此时得分减少量为 \(0\)(因为只有一个数的区间的和与最大值就是区间所含的这个数本身,相减即为 \(0\)),显然不优。因此 \(0\) 不应该选。

    吐槽一句:选不选 \(0\) 不应该是小 X 的事情么?什么时候轮到小 Y 来决定选什么了(笑)。

#include <bits/stdc++.h>
using namespace std; #define ll long long
#define mem(x,v) memset(x,v,sizeof(x))
#define pii pair <int,int>
#define fi first
#define se second const int N=1e5+5;
const ll W=1ll<<31; ll n,q,ans,u[N],d[N]; // SegTree 1
ll mval[N<<2];
void build(int l,int r,int x){
if(l==r)return mval[x]=d[l],void();
int m=l+r>>1;
build(l,m,x<<1),build(m+1,r,x<<1|1);
mval[x]=max(mval[x<<1],mval[x<<1|1]);
}
void modify(int l,int r,int p,int x,ll v){
if(l==r)return mval[x]=v,void();
int m=l+r>>1;
if(p<=m)modify(l,m,p,x<<1,v);
else modify(m+1,r,p,x<<1|1,v);
mval[x]=max(mval[x<<1],mval[x<<1|1]);
}
ll query(int l,int r,int ql,int qr,int x){
if(ql<=l&&r<=qr)return mval[x];
ll m=l+r>>1,ans=-W;
if(ql<=m)ans=max(ans,query(l,m,ql,qr,x<<1));
if(m<qr)ans=max(ans,query(m+1,r,ql,qr,x<<1|1));
return ans;
} // BIT
ll c[N];
void add(int x,ll v){while(x<=n)c[x]+=v,x+=x&-x;}
ll query(int x){ll s=0; while(x)s+=c[x],x-=x&-x; return s;}
ll query(int l,int r){return query(r)-query(l-1);} // end points
set <pii> s;
ll cont[N]; // SegTree 2
ll R,node,ls[N<<6],rs[N<<6],val[N<<6],sum[N<<6];
void insert(ll l,ll r,ll p,ll &x,bool tp){
if(!x)x=++node;
if(tp)sum[x]++,val[x]+=p-W;
else sum[x]--,val[x]-=p-W;
if(l==r)return;
ll m=l+r>>1;
if(p<=m)insert(l,m,p,ls[x],tp);
else insert(m+1,r,p,rs[x],tp);
}
ll query(ll l,ll r,ll k,ll x){
if(!x)return -W;
if(l==r)return k+val[x]<=0?((k-1)/(W-l)+1):-W;
ll m=l+r>>1;
if(k+val[ls[x]]<=0)return query(l,m,k,ls[x]);
return sum[ls[x]]+query(m+1,r,k+val[ls[x]],rs[x]);
} void clear(pii x){
if(x.fi==1||x.se==n-1)return;
insert(1,W,cont[x.fi]+W,R,0),cont[x.fi]=0;
}
void update(int l,int r){
if(l==1||r==n-1)return;
cont[l]=query(l,r)-query(1,n,l,r,1);
insert(1,W,cont[l]+W,R,1);
} // update changes
void change(int pos,ll &val,ll add){
auto it=s.upper_bound({pos,N}); // update end points and contribution
if(val<=0&&val+add>0){
ans+=val+add;
pii x=*--it; clear(x),s.erase(x);
if(x.fi==x.se)return val+=add,void();
else if(x.fi==pos)s.insert({++x.fi,x.se}),update(x.fi,x.se);
else if(pos==x.se)s.insert({x.fi,--x.se}),update(x.fi,x.se);
else{
s.erase(x);
s.insert({x.fi,pos-1}),update(x.fi,pos-1);
s.insert({pos+1,x.se}),update(pos+1,x.se);
}
}
else if(val>0&&val+add<=0){
ans-=val;
pii x={pos,pos};
if(it!=s.begin()){
pii tmp=*(--it); it++;
if(tmp.se==pos-1)clear(tmp),s.erase(tmp),x.fi=tmp.fi;
} if(it!=s.end()){
pii tmp=*it;
if(tmp.fi==pos+1)clear(tmp),s.erase(tmp),x.se=tmp.se;
} s.insert(x),update(x.fi,x.se);
} else if(val<=0&&val+add<=0)it--,clear(*it),update((*it).fi,(*it).se);
else ans+=add; val+=add;
} void solve(){
// empty
mem(c,0),mem(cont,0),s.clear(),ans=0;
R=node,mem(ls,0),mem(rs,0),mem(val,0),mem(sum,0); // read
cin>>n>>q>>u[0];
for(int i=1;i<n;i++){
cin>>u[i],d[i]=u[i]-u[i-1];
if(d[i]>0)ans+=d[i];
add(i,d[i]);
} build(1,n,1); // find end points [l,r)
for(int i=1,pre=1;i<n;i++){
if(d[i]<=0&&d[i-1]>0)pre=i;
if(d[i]>0&&d[i-1]<=0){
s.insert({pre,i-1});
if(pre!=1)update(pre,i-1);
} if(i==n-1&&d[i]<=0)s.insert({pre,n-1});
} // answer all the queries
for(int i=1;i<=q;i++){
int op; cin>>op;
if(op==0){
ll l,r,c; cin>>l>>r>>c,l--,r--; // modify SegTree 1 & BIT then update answer
if(l)add(l,c),modify(1,n,l,1,d[l]+c),change(l,d[l],c);
if(r+1<n)add(r+1,-c),modify(1,n,r+1,1,d[r+1]-c),change(r+1,d[r+1],-c);
}
else cout<<ans<<" "<<(ans?max(-1ll,query(1,W,ans,R)):0)<<endl;
}
}
int main(){
int T; cin>>T;
while(T--)solve();
return 0;
}

P4497 [WC2011]拼点游戏的更多相关文章

  1. 洛谷 P4497 - [WC2011]拼点游戏(数据结构综合)

    题面传送门 神仙 DS. 首先关于第一问可以轻松想到一个 DP,\(dp_{i,j}\) 表示考虑到第 \(i\) 位,这一位奇偶性为 \(j\) 的最大权值,时间复杂度 \(n^2q\),可以拿到 ...

  2. C#的winform拼数字游戏

    C#的winform拼数字游戏 声明:阅读了别人的代码学习修改而来,增加了美观度和游戏乐趣.(作者出处忘了不好意思) 程序截图 关键代码 using System; using System.Coll ...

  3. 【OpenJ_Bailian - 4005】拼点游戏(贪心)

    拼点游戏 Descriptions: C和S两位同学一起玩拼点游戏.有一堆白色卡牌和一堆蓝色卡牌,每张卡牌上写了一个整数点数.C随机抽取n张白色卡牌,S随机抽取n张蓝色卡牌,他们进行n回合拼点,每次两 ...

  4. H5页游戏内存溢出问题

    记录自己解决的第一个H5页的性能问题, 关于内存溢出 拼字游戏 问题表现 初始化后, 第一次拼字并不卡. 随着拼的次数越来越多, 越来越卡 浏览器任务管理器中可以看出, 内存持续升高 确定内存问题, ...

  5. 妙啊!纯 CSS 实现拼图游戏

    本文,将向大家介绍一种将多个 CSS 技巧运用到极致的技巧,利用纯 CSS 实现拼图游戏. 本技巧源自于 Temani Afif 的 CodePen CSS Only Puzzle game.一款完全 ...

  6. Java正则表达式应用详解

    如果你曾经用过Perl或任何其他内建正则表达式支持的语言,你一定知道用正则表达式处理文本和匹配模式是多么简单.如果你不熟悉这个术语,那么“正则表达式”(Regular Expression)就是一个字 ...

  7. 正则表达式 java

    如果你曾经用过Perl或任何其他内建正则表达式支持的语言,你一定知道用正则表达式处理文本和匹配模式是多么简单.如果你不熟悉这个术语,那么"正则表达式"(Regular Expres ...

  8. 【转】java正则表达式

    在Sun的Java JDK 1.40版本中,Java自带了支持正则表达式的包,本文就抛砖引玉地介绍了如何使用java.util.regex包. 可粗略估计一下,除了偶尔用Linux的外,其他Linu ...

  9. matlab的正则表达式讲解[转]

    引言.啥是正则表达式?正则表达式是干啥的?我理解就和我们在word或者其他编辑软件里点的查找.替换的作用是差不多的,不过功能要强大的多,当然使用起来也稍微复杂一些.书上的定义差不多是这样的:正则表达式 ...

随机推荐

  1. 欧姆龙PLC HostLink协议整理

    欧姆龙PLC HostLink协议整理 1.常用的存储器功能区 CIO: 输入继电器  272 点(17 CH) 0.00-16.15 输出继电器  272 点(17 CH) 100.00-116.1 ...

  2. Codeforces Round #573 (Div. 2) D题题解

    一.题目 ​ Tokitsukaze, CSL and Stone Game ​ Tokitsukaze和CSL正在玩一些石头游戏. ​ 一开始,有n堆的石头,第i堆石头数记为 \(a_i\),两人轮 ...

  3. [对对子队]团队任务拆解Alpha

    Alpha阶段主要目标 完成游戏场景的基本实现(不要求美术资源) 完成游戏UI的基本实现(不要求美术资源) 制作第一部分的关卡(顺序语句,制作3-5关) 完成第一部分关卡和游戏基本逻辑的测试 任务分解 ...

  4. 第32篇-解析interfacevirtual字节码指令

    在前面介绍invokevirtual指令时,如果判断出ConstantPoolCacheEntry中的_indices字段的_f2属性的值为空,则认为调用的目标方法没有连接,也就是没有向Constan ...

  5. 模拟赛18 T1 施工 题解

    前言: 真的是不容易啊.这个题在考场上想到了最关键的性质,但是没写出来. 后来写出来,一直调,小错不断. 没想到改的最后一个错误是两个int 乘起来爆了int 其实最后我还是觉得复杂度很假.\(n^2 ...

  6. IDA*、操作打表、并行处理-The Rotation Game HDU - 1667

    万恶之源 优秀题解 用文字终究难以穷尽代码的思想 思路 每次操作都有八种选择,相当于一棵每次延申八个子节点的搜索树,故搜索应该是一种方法.而这题要求求最少步数,我们就可以想到可以试试迭代加深搜索(但其 ...

  7. 微信小程序API接口封装

    @ 目录 一,让我们看一下项目目录 二,让我们熟悉一下这三个文件目的(文件名你看着办) 三,页面js中如何使用 今天的API的封装,我们拿WX小程序开发中,对它的API (wx.request)对这个 ...

  8. Linux下向windows传输文件【sz 文件】没有弹框提示下载到什么位置

    Linux环境向windows环境传输文件 security crt工具,同同一个软件,连接不同服务器,有的服务器传送文件没有弹框选择要下载的文件路径,可以在[Options]-[Session Op ...

  9. SpringCloud升级之路2020.0.x版-30. FeignClient 实现重试

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 需要重试的场景 微服务系统中,会遇到在线发布,一般的发布更新策略是:启动一个新的,启动成功 ...

  10. Java反射判断对象实例所有属性是否为空

    https://www.jb51.net/article/201647.htm public static Boolean ObjectAllFieldsEmpty(Object obj) throw ...