4.11省选模拟
难~
T1
想到了优化前的dp
40pts无优化
#include<bits/stdc++.h>
#define INF 2147483647
#define MAXN 1005
using namespace std;
int n,a[MAXN],dp[MAXN],Max[MAXN],Mid[MAXN];
int main()
{
freopen("separate.in","r",stdin);
freopen("separate.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
Max[i]=max(Max[i-1],a[i]);
}
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
{
memcpy(Mid,dp,sizeof(dp));
memset(dp,0x3f,sizeof(dp));
for(int j=0;j<i;j++)
{
int now;
if(a[i]>=a[j]) now=i;
else now=j;
dp[now]=min(dp[now],Mid[j]+a[now]);
dp[j]=min(dp[j],Mid[j]+Max[i]);
//其实转移就有两种
//第二个整体加不管
//第一个也分为两种情况,如果a[j]>a[i]那么所有大于a[i]的位置区间加a[j]
//貌似是可以用线段树维护的,但是太麻烦,或许也能cdq或者决策单调性?
//弃了,40跑路.
}
}
int Ans=INF;
for(int i=0;i<=n;i++)
{
Ans=min(Ans,dp[i]);
}
cout<<Ans<<"\n";
}
把转移写出来
重新考虑一下我们这一维的含义,其实可以看成是第二维表示成数字大小
那么
dp[i][j]表示前i个点,一个序列末端是j,另一个序列末端是s[i]的最小士气值
转移易得
a[i+1]>s[i],dp[i+1][j]=dp[i][j]+a[i+1]
a[i+1]<=s[i]
Sit1:
a[i+1]<j,dp[i+1][j]=dp[i+1][j]+j区间加等差数列
Sit2
a[i+1]>=j,dp[i+1][a[i+1]]=dp[i][j]+a[i+1]找一个区间最小值加进去
dp[i+1][j]=dp[i][j]+s[i]区间加
由于区间加等差数列可以看成斜率,找一个凸包最下面的点就好了,显然
直接大力分块,维护凸包即可
#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define MAXN 70001
#define MAXM 1001
using namespace std;
int blo,sy[MAXN],L[MAXM],R[MAXM],nowp[MAXM];
int n,a[MAXN],s[MAXN];
ll lazy[MAXM],mx[MAXM],val[MAXN],ch[MAXM];
vector<int> vec[MAXM];
inline double slope(int x,int y) {return 1.0*(val[x]-val[y])/(x-y);}
void push_down(int x)
{
for(int i=L[x];i<=R[x];++i) val[i]+=1ll*i*ch[x]+lazy[x];
ch[x]=lazy[x]=0;
}
void remake(int x)
{
push_down(x);
vec[x].clear();
for(int i=L[x];i<=R[x];++i)
{
if(vec[x].size()&&val[i]>=val[vec[x][vec[x].size()-1]]) continue;
while(vec[x].size()>1&&slope(i,vec[x][vec[x].size()-1])<=slope(vec[x][vec[x].size()-1],vec[x][vec[x].size()-2])) vec[x].erase(--vec[x].end());
vec[x].push_back(i);
}
nowp[x]=vec[x].size()-1;
mx[x]=val[vec[x][nowp[x]]];
}
void repair(int x)
{
while(nowp[x]!=0&&val[vec[x][nowp[x]]]+ch[x]*vec[x][nowp[x]]>=val[vec[x][nowp[x]-1]]+ch[x]*vec[x][nowp[x]-1]) --nowp[x];
mx[x]=val[vec[x][nowp[x]]]+ch[x]*vec[x][nowp[x]];
}
ll query(int l,int r)
{
ll res=INF;
if(sy[l]==sy[r])
{
repair(sy[l]);
for(int i=l;i<=r;++i) res=min(res,i*ch[sy[i]]+val[i]+lazy[sy[i]]);
return res;
}
for(int i=sy[l];i<=sy[r];++i) repair(i);
for(int i=l;i<=R[sy[l]];++i) res=min(res,i*ch[sy[i]]+val[i]+lazy[sy[i]]);
for(int i=r;i>=L[sy[r]];--i) res=min(res,i*ch[sy[i]]+val[i]+lazy[sy[i]]);
for(int i=sy[l]+1;i<sy[r];++i) res=min(res,mx[i]+lazy[i]);
return res;
}
void add(int l,int r,int v)
{
if(sy[l]==sy[r])
{
for(int i=l;i<=r;++i) val[i]+=v;
remake(sy[l]);
return;
}
for(int i=l;i<=R[sy[l]];++i) val[i]+=v;
for(int i=r;i>=L[sy[r]];--i) val[i]+=v;
remake(sy[l]);remake(sy[r]);
for(int i=sy[l]+1;i<sy[r];++i) lazy[i]+=v;
return ;
}
void addc(int l,int r)
{
if(sy[l]==sy[r])
{
for(int i=l;i<=r;++i) val[i]+=i;
remake(sy[l]);
return;
}
for(int i=l;i<=R[sy[l]];++i) val[i]+=i;
for(int i=r;i>=L[sy[r]];--i) val[i]+=i;
remake(sy[l]);remake(sy[r]);
for(int i=sy[l]+1;i<sy[r];++i) ch[i]++;
return ;
}
void re()
{
for(int i=1;i<=sy[n];++i) push_down(i);
for(int i=1;i<=sy[n];++i) remake(i);
}
void out()
{
re();
// cout<<sy[i]<<" "<<L[sy[i]]<<" "<<R[sy[i]]<<endl;
for(int i=0;i<=n;++i) cout<<val[i]<<" ";
cout<<endl;
}
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
signed main()
{
freopen("separate.in","r",stdin);
freopen("separate.out","w",stdout);
memset(val,0x3f,sizeof(val));
val[0]=0;
n=read();
for(int i=1;i<=n;++i) a[i]=read();
blo=sqrt(n);
for(int i=0;i<=n;++i)
{
sy[i]=(i/blo)+1;
if(!L[sy[i]]) L[sy[i]]=i;
R[sy[i]]=i;
}
L[1]=0;
for(int i=1;i<=sy[n];++i) remake(i);
for(int i=1;i<=n;++i) s[i]=max(s[i-1],a[i]);
for(int i=0;i<n;++i)
{
if(a[i+1]>s[i])
{
add(0,a[i+1],a[i+1]);
}
else
{
if(a[i+1]+1<=s[i]) addc(a[i+1]+1,s[i]);
ll p=query(0,a[i+1])+a[i+1];
if(0<=a[i+1]-1) add(0,a[i+1]-1,s[i]);
push_down(sy[a[i+1]]);
val[a[i+1]]=p;
remake(sy[a[i+1]]);
}
}
for(int i=1;i<=sy[n];++i) remake(i);
ll res=INF;
for(int i=0;i<=n;++i) res=min(res,val[i]);
cout<<res;
}
T2
正解竟然是搜索....貌似是基于border集合大小有限得出的
暴力dfs
#include<bits/stdc++.h>
using namespace std;
int n,k,Fin,Ans[1000];
void dfs(int now)
{
if(now==n+1)
{
int res=0;
for(int j=2;j<=n;j++)
{
int len=0;
while(Ans[1+len]==Ans[j+len]) len++;
res=max(res,len);
}
Fin+=res;
return ;
}
for(int i=1;i<=k;i++)
{
Ans[now]=i;
dfs(now+1);
}
}
int main()
{
freopen("lorem.in","r",stdin);
freopen("lorem.out","w",stdout);
scanf("%d%d",&n,&k);
dfs(1);
cout<<Fin<<"\n";
}
正解需要Border理论,不会
T3
重新透彻了一下模拟费用流,考场上一直误以为是dp,导致根本没想到
其实dp的过程就是找匹配,思想很接近模拟费用流了,但是没有转化到匹配上
粘一下暴力dp
#include<bits/stdc++.h>
#define int long long
#define MAXN 1005
#define INF LLONG_MAX
using namespace std;
int a[MAXN],b[MAXN],Ans=INF,n,k;
int dp[MAXN][MAXN],Min[MAXN][MAXN],Mid[MAXN][MAXN];
signed main()
{
freopen("world.in","r",stdin);
freopen("world.out","w",stdout);
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&b[i]);
}
// if(n>1000)
// {
// priority_queue<int,vector<int>,greater<int> >q;
// for(int i=1;i<=n;i++)
// {
// q.push(a[i]+b[i]);
// }
// int cnt=k,Ans=0;
// while(cnt--)
// {
// Ans+=q.top();
// q.pop();
// }
// cout<<Ans;
// return 0;
// }
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=n;i++)//枚举目前选到哪个位置了
{
memcpy(Mid,dp,sizeof(Mid));
// memset(dp,0x3f,sizeof(dp));
for(int j=0;j<=min(i-1,k-1);j++)//枚举我们目前选了几个人了
{
// for(int z=j;z<i;z++)//枚举上面一层上一个选的是哪个
// {
for(int v=j+1;v<=i;v++)
{
dp[j+1][v]=min(dp[j+1][v],min(Mid[j+1][v],Mid[j][v-1]+a[v]+b[i]));
dp[j+1][v]=min(dp[j+1][v],dp[j+1][v-1]);
if(j+1==k)
{
Ans=min(Ans,dp[j+1][v]);
}
continue;
}
// dp[j+1][Min[z+1][i]]=min(dp[j+1][Min[z+1][i]],dp[j][z]+a[Min[z+1][i]]+b[i]);
// //区间是z+1,i
// }
}
}
cout<<Ans<<"\n";
}
正解
#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define INF 10000000000
#define int long long
#define MAXN 500005
using namespace std;
int a[MAXN],b[MAXN],Sum,num,n,k;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
bool check(int mid)
{
while(q.size())q.pop();
int res=0;
num=0;
Sum=0;
for(int i=1;i<=n;i++)
{
q.push(make_pair(a[i],0));
int now=q.top().first;
int id=q.top().second;
// cout<<now<<" "<<id<<"\n";
if(b[i]+now+mid>0)
{
continue;
}
Sum+=b[i]+now+mid;
if(id==0) num++;
q.pop();
q.push(make_pair(-b[i]-mid,1));
}
return num>=k;
}
signed main()
{
freopen("world.in","r",stdin);
freopen("world.out","w",stdout);
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
int l=-INF,r=INF,mid,Ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
//如果大于等于返回true
if(check(mid))
{
l=mid+1;
Ans=mid;
}
else
{
r=mid-1;
}
}
check(Ans);
cout<<Sum-k*Ans<<"\n";
}
__EOF__

本文作者:Eternal_Battle
本文链接:https://www.cnblogs.com/Eternal-Battle/p/16130719.html
关于博主:这个世界除了你,都知道我喜欢你
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/Eternal-Battle/p/16130719.html
关于博主:这个世界除了你,都知道我喜欢你
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具