P5665 [CSP-S2019] 划分
思路:
首先求出 \(a\) 的前缀和数组 \(s\)。
考虑动态规划,令 \(dp_{i,j}\) 表示以 \(i\) 结尾,末尾有 \(j\) 个为一组的最小答案,则状态转移方程为:
\]
朴素直接转移是 \(O(N^3)\) 的,可以得到 36pts 的好成绩代码就懒的给了。
考虑优化,对于求出最小的一个 \(k\),使得 \(s_{i-j}-s_{i-j-k} > s_i - s_{i-j}\),那么状态转移方程为:
\]
后面的一串可以提前前缀预处理好,现在的复杂度在求 \(k\) 上,注意到 \(s_{i,j} - s_{i-j-k}\) 是单调的,那么直接二分即可。
时间复杂度优化至 \(O(N^2 \log N)\)。
$O(N^2 \log N)$ 代码
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=5050,INF=4e18;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
bool op;
ll n,l,r,h,t,ans=INF;
ll s[N],dp[N][N],f[N][N];
ll get(ll l,ll r){
if(l>r)
return 0;
if(!l)
return s[r];
return s[r]-s[l-1];
}
bool End;
int main(){
n=read(),op=read();
for(int i=1;i<=n;i++)
s[i]=s[i-1]+read();
dp[1][0]=f[1][0]=INF;
dp[1][1]=f[1][1]=s[1]*s[1];
for(int i=2;i<=n;i++){
f[i][0]=dp[i][0]=INF;
for(int j=1;j<=i;j++){
l=1,r=i-j,t=0,h=get(i-j+1,i);
if(s[i-j]<=h)
t=i-j+1;
else{
while(l<=r){
ll mid=(l+r)>>1;
if(get(i-j-mid+1,i-j)>h){
t=mid;
r=mid-1;
}
else
l=mid+1;
}
}
dp[i][j]=f[i-j][t-1]+h*h;
f[i][j]=min(f[i][j-1],dp[i][j]);
}
}
for(int i=1;i<=n;i++)
ans=min(ans,dp[n][i]);
write(ans);
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
return 0;
}
之后我们可以发现,若 \(j\) 的单增的,则 \(i-j-k+1\) 是单降的,那么我们直接对 \(k\) 进行走指针即可,时间复杂度优化至 \(O(N^2)\),可以拿到 64pts 的好成绩。
$O(N^2)$ 代码
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=5050,INF=4e18;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
bool op;
ll n,t,h,sum,ans=INF;
ll s[N],a[N],dp[N][N],f[N][N];
ll get(ll l,ll r){
if(l>r)
return 0;
if(!l)
return s[r];
return s[r]-s[l-1];
}
bool End;
int main(){
n=read(),op=read();
for(int i=1;i<=n;i++){
a[i]=read();
s[i]=s[i-1]+a[i];
}
dp[1][0]=f[1][0]=INF;
dp[1][1]=f[1][1]=s[1]*s[1];
for(int i=2;i<=n;i++){
f[i][0]=dp[i][0]=INF;
t=i-1,sum=a[i-1];
for(int j=1;j<=i;j++){
ll h=get(i-j+1,i);
while(sum<=h&&t){
t--;
sum+=a[t];
}
dp[i][j]=f[i-j][i-j-t]+h*h;
f[i][j]=min(f[i][j-1],dp[i][j]);
sum-=a[i-j];
}
}
for(int i=1;i<=n;i++)
ans=min(ans,dp[n][i]);
write(ans);
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
return 0;
}
因为我们这种 dp 的状态数都已经达到了 \(N^2\),于是考虑找一些性质。
容易打表发现在合法情况下,满足 \(dp_{i,j} \le dp_{i,j+1}\)。
那么我们可以找到每个位置 \(i\),记录一下 \(f_i\) 表示 \(\min dp_{i,j}\),且最后一段为 \([g_i,i]\),则状态转移方程为:
\]
此时我们就将状态时将至 \(O(N)\) 级别,现在考虑来优化状态转移方程。
$O(N)$ 状态代码
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=5e5+10,INF=4e18;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
bool op;
ll n,t,h,ans=INF;
ll s[N],a[N],f[N],g[N];
ll get(ll l,ll r){
if(l>r)
return 0;
if(l<0)
return s[r];
return s[r]-s[l-1];
}
bool End;
int main(){
n=read(),op=read();
for(int i=1;i<=n;i++){
a[i]=read();
s[i]=s[i-1]+a[i];
f[i]=INF;
}
g[1]=1;
f[0]=g[0]=0;
f[1]=s[1]*s[1];
for(int i=2;i<=n;i++){
for(int j=0;j<i;j++){
h=get(g[j],j),t=get(j+1,i);
if(h>t)
continue;
if(f[j]+t*t<f[i]){
f[i]=f[j]+t*t;
g[i]=j+1;
}
}
}
write(f[n]);
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
return 0;
}
容易发现,当 \(j\) 最大时,这个式子的值最小,所以我们需要求出一个最大的 \(j\) 满足 \(s_j-s_{g_j-1} \le s_i - s_j\),即:
\]
注意到 \(s_i\) 单增,我们可以维护一个 \(2s_j - s_{g_j-1}\) 单增的单调队列,然后找到这个队列最后一个满足条件的 \(j\),那么 \(j\) 以前的数对答案无法造成贡献,将其弹出。
这样每个数至多被弹出一次,时间复杂度为 \(O(N)\)。
完整代码:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
typedef __int128 __;
bool Begin;
const ll N=4e7+5,mod=1ll<<30;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(__ x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
__ t,ans;
bool op;
int n,head=1,tail=0;
ll s[N],g[N];
int Q[N];
inline void Read(){
ll x,y,z,m,p,l,r,pre=0;
x=read(),y=read(),z=read(),s[1]=read(),s[2]=read(),m=read();
for(int i=3;i<=n;i++)
s[i]=(x*s[i-1]+y*s[i-2]+z)%mod;
for(int i=1;i<=m;i++){
p=read(),l=read(),r=read();
for(int j=pre+1;j<=p;j++)
s[j]=(s[j]%(r-l+1))+l;
pre=p;
}
}
inline ll get(int l,int r){
if(l>r)
return 0;
if(l<1)
return s[r];
return s[r]-s[l-1];
}
inline ll date(ll x){
return 2ll*s[x]-s[g[x]-1];
}
bool End;
int main(){
n=read(),op=read();
if(op==1)
Read();
else{
for(int i=1;i<=n;i++)
s[i]=read();
}
for(int i=1;i<=n;i++)
s[i]+=s[i-1];
g[1]=1,g[0]=0;
Q[++tail]=0,Q[++tail]=1;
for(int i=2;i<=n;i++){
while(date(Q[head+1])<=s[i]&&head+1<=tail)
head++;
g[i]=Q[head]+1;
t=get(g[i],i);
while(date(i)<=date(Q[tail])&&tail>=head)
tail--;
Q[++tail]=i;
}
for(int i=n;i>=1;i=g[i]-1)
ans+=(__)(s[i]-s[g[i]-1])*(s[i]-s[g[i]-1]);
write(ans);
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
return 0;
}
P5665 [CSP-S2019] 划分的更多相关文章
- 【CSP-S 2019】【洛谷P5665】划分【单调队列dp】
前言 \(csp\)时发现自己做过类似这道题的题目 : P4954 [USACO09Open] Tower of Hay 干草塔 然后回忆了差不多\(15min\)才想出来... 然后就敲了\(88p ...
- P5665 划分
Part 1 先来看一个错误的贪心做法:假设当前结尾的一段和为 \(a\),等待加入结尾的一段和为 \(b\),现在要处理新进来的数 \(c\). \(a\leq b\),将 \(a\) 算入答案,将 ...
- 洛谷 P5665 [CSP-S2019] 划分
链接: P5665 题意: 给出 \(n\) 个整数 \(a_i\) ,你需要找到一些分界点 \(1 \leq k_1 \lt k_2 \lt \cdots \lt k_p \lt n\),使得 \( ...
- [P5665][CSP2019D2T2] 划分
先说说部分分做法吧 1.\(n \leq 10\) 指数级瞎草都可以2333 2.\(n \leq 50\) 好像并没有什么做法-也许给剪枝的人部分分吧 3.\(n \leq 400\) 这个复杂度是 ...
- CCF CSP 201612-2 工资计算
CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201612-2 工资计算 问题描述 小明的公司每个月给小明发工资,而小明拿到的工资为交完个人所 ...
- xss之上传文件的xss,绕过csp,预警机制
xss1.XSS姿势——文件上传XSS https://wooyun.x10sec.org/static/drops/tips-14915.html总结: 1.1.文件名方式,原理:有些文件名可能反应 ...
- CSP-J/S2019试题选做
S D1T2 括号树 设\(f[u]\)表示根到\(u\)的路径上有多少子串是合法括号串.(即题目里的\(k_u\),此变量名缺乏个性,故换之) 从根向每个节点dfs,容易求出\(c[u]\):表示从 ...
- [LeetCode] Partition List 划分链表
Given a linked list and a value x, partition it such that all nodes less than x come before nodes gr ...
- SWMM模型子汇水区划分的几种方法
子汇水区的划分是SWMM模型建模的主要步骤之一,划分的好坏对结果精度有比较大的影响.概括来讲,子汇水区的划分有以下几种思路: (1)根据管网走向.建筑物和街道分布,直接人工划分子汇水区.这个方法适用于 ...
- 等价类划分方法的应用(jsp)
[问题描述] 在三个文本框中输入字符串,要求均为1到6个英文字符或数字,按submit提交. [划分等价类] 条件1: 字符合法; 条件2: 输入1长度合法; 条件3: 输入2长度合法: 条件4: 输 ...
随机推荐
- Opencv笔记(12)傅里叶变换
在之前了解的OpenCV为我们实现的图像变换,这些本质上是从图像到输出图像的映射,即输入仍是一幅图像.本章的傅里叶变换,输出数组的值在含义上和原图像的强度值大不相同,是输入图像的频域表示. cv::d ...
- OOP课第二阶段总结
OOP课第二阶段总结 前言 作为第二次3+1的总结,明显感受到了此次题目集越来越复杂,结合了实际的物理知识来解决现实中的电路问题.因为电路可以一直扩展下去,情况千变万化,难以像上次题目集一样找到一个呆 ...
- 什么是LAMP
LAMP Lamp是一种互联网开发架构,全称为LAMP Stack(Linux.Apache.MySQL.PHP).它是一种流行的Web开发环境,用于构建动态网站和Web应用程序.LAMP Stack ...
- ASPNET Core Docker 运行慢报错 C:\Users\<username>\vsdbg\vs2017u5”无效
ASPNET Core Docker 运行慢报错 C:\Users\<username>\vsdbg\vs2017u5"无效 16.9.20122.2 1.迅雷下载: https ...
- 夜莺项目发布 v6.1.0 版本,增强可观测性数据串联
大家好,夜莺项目发布 v6.1.0 版本,这是一个中版本迭代,不止是 bugfix 了,而是引入了既有功能的增强.具体增强了什么功能,下面一一介绍. 1. 增强可观测性数据串联 从 v6.1.0 开始 ...
- rust程序设计(4)关于 trait | impl 相关的概念和疑问
trait是什么? Rust中的trait是一种定义可被多种类型实现的共享行为的方式.它类似于Java或C#中的接口.通过trait,你可以定义一组方法签名(有时包括默认实现),不同的类型可以实现这些 ...
- 一个常见的 JavaScript 解构陷阱
在日常的 JavaScript 编码中,我们经常使用解构语法来提取对象中的属性.假设我们有一个名为 fetchResult 的对象,代表从接口返回的数据,其中包含一个字段名为 data. const ...
- Java对称加解密算法AES
Java对称加解密算法AES import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.String ...
- Json输出List集合对象和map对象 JSON格式
Json输出List集合对象和map对象 JSON格式 //Json输出List集合对象 [{"属性1":["值1"],"属性2":&quo ...
- 盘点 Spring Boot 解决跨域请求的几种办法
熟悉 web 系统开发的同学,对下面这样的错误应该不会太陌生. 之所以会出现这个错误,是因为浏览器出于安全的考虑,采用同源策略的控制,防止当前站点恶意攻击 web 服务器盗取数据. 01.什么是跨域请 ...