P5665 [CSP-S2019] 划分

news/2024/9/18 7:03:16/文章来源:https://www.cnblogs.com/rgw2010/p/18336475

思路:

首先求出 \(a\) 的前缀和数组 \(s\)

考虑动态规划,令 \(dp_{i,j}\) 表示以 \(i\) 结尾,末尾有 \(j\) 个为一组的最小答案,则状态转移方程为:

\[dp_{i,j} = \min [s_{i-j}-s_{i-j-k} \le s_i - s_{i-j}] dp_{i-j,k} + (s_i - s_{i-j})^2 \]

朴素直接转移是 \(O(N^3)\) 的,可以得到 36pts 的好成绩代码就懒的给了

考虑优化,对于求出最小的一个 \(k\),使得 \(s_{i-j}-s_{i-j-k} > s_i - s_{i-j}\),那么状态转移方程为:

\[dp_{i,j} = (s_i - s_{i-j})^2 + \min\limits_{l=1}^k dp_{i-j,l} \]

后面的一串可以提前前缀预处理好,现在的复杂度在求 \(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;}elsel=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]\),则状态转移方程为:

\[f_i = \min\limits_{j=0}^{i-1} [s_j-s_{g_j-1} \le s_i - s_j] f_j + (s_i - s_j)^2 \]

此时我们就将状态时将至 \(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\),即:

\[2s_j - s_{g_j-1} \le s_i \]

注意到 \(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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/776355.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

「代码随想录算法训练营」第二十六天 | 贪心算法 part4

452. 用最少数量的箭引爆气球题目链接:https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/ 题目难度:中等 文章讲解:https://programmercarl.com/0452.用最少数量的箭引爆气球.html 视频讲解:https://www.bilibili.com/video/BV1SA41167xe 题目状态…

dbnet crnn java中文ocr识别

Table of ContentsAbout Getting Started Result ContactAbout完整项目:https://github.com/jiangnanboy/dbnet_crnn_java本项目利用java,javacv,onnx以及djl矩阵计算等技术加载文本检测模型dbnet与文本识别模型crnn,完成ocr的识别推理。 包含模型的完整项目请从右侧releases…

变量及标识符

变量 变量的概念 内存中的一个存储区域 该区域的数据可以在同一类型范围内不断变化 变量是程序中最基本的存储单元。包含变量类型、变量名和存储的值 变量的作用: 用于在内存中保存数据 使用变量注意: Java中每个变量必须先声明,后使用 使用变量名来访问这块区域的数据 变量…

如何通过PowerShell批量修改O365用户的office phone属性值

我的博客园:https://www.cnblogs.com/CQman/ 如何通过PowerShell批量修改O365用户的office phone属性值? 需求信息:组织中的O365用户在创建时,已手动录入了办公电话(Office phone),现在需要在办公电话前面加上统一的数字,如“0571-0985”,以批量的方式统一修改。 备注:…

1000W长连接,如何建立和维护?千万用户IM 架构设计

文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备 免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,…

封装,private关键字,this关键字

我们上一个案例,使用private关键字将成员进行修饰,外界无法直接访问,讲了那么长时间,实际上就是在传输一个思想面向对象编程的三大特征,第一大特征:封装封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。private关键字:1、被private修饰的成员,外界无法直…

Anaconda 常用命令

1.检查Anaconda是否成功安装:conda --version2.检测目前安装了哪些环境:conda info --envs3.检查目前有哪些版本的python可以安装:conda search --full-name python4.安装不同版本的python:conda create --name tensorflow python=3.75.按照提示,激活python环境:activate…

2024暑假集训测试17

前言比赛链接。T1 没加记忆化莫名原因 T 飞了,T2 没做过 IO 交互不知道咋测样例干脆没交,T3 到现在还不知道为啥爆零了,赛时不知道咋合并背包根本不敢打,离线下来寻思快点结果全死了,T4 不可做题。 还是老毛病,遇到之前见的不多题型(尤其是 T1、T2 放)就寄,这次 T1 倒…

『模拟赛』暑假集训CSP提高模拟13

『模拟赛记录』暑假集训CSP提高模拟13Rank 上半最后一次正式模拟赛,感觉还彳亍A. 小孩召开法1 原[ABC278F] Shiritori 签到题。 博弈论+状压+记搜秒了,感觉不用太细说。 不过是暑假以来第一次首 A 啊,开始还胡乱想 SG 定理的做法,后来发现不用那么复杂。点击查看代码 #incl…