P1070 [NOIP2009 普及组] 道路游戏

传送门

思考最朴素做法
fi,j,p表示在第i个时刻终点为j且机器人走了p步获得的最大金币数,则有:

fi,j,p=rw(j1),i+{fi1,w(j1),p1p>0maxwz=1nmaxk=0min(i1,p)(fi1,wz,kGj)p=0

w(x)=(xmodn+n)modn==0?n:(xmodn+n)modn

其中,ri,j表示第i条路第j个时刻的金币,Gi表示在i个工厂买机器人的金币

初始化:i=1nf0,i,0=Gi

我们发现可以降维,第三维其实可以用前缀和维护。

fi,j表示在第i个时刻走完后在工厂j买了机器的最大金币数

定义sumi,j表示终点为i且走了j个时刻的金币

sumi,j=sumw(i1),j1+rw(i1),j

定义函数val(st,la,ti)=sumw(st+ti),la+tisumst,la表示在第st个时刻第la个工厂开始走走了ti个时刻在道路上获得的金币,则有

fi.j=Gj+maxst=1nmaxti=1min(i,p)(fiti,st+val(st,iti,ti))

时间复杂度O(n3)需要优化

考虑单调队列

该单调区间内记录gs,ks,pos,其中gs表示买上一个机器人时获得的最大金币数为gs(注意,记录的是买了后的最大金币数),ks表示上一个机器人在第ks个工厂买,pos表示在第pos个时刻买

求答案时取队首的q,则ans=qgs+val(qks,qpos,iqpos)

然而单调队列要满足一个性质无论何时都要满足单调性,所以我们发现不可以把所有的数都放到同一个单调队列,因为不同道路上的金币不同的时刻可能不同,若我们把他们放到同一个单调队列,可能会导致在第一个时刻从工厂一开始会更优,第二个时刻的时候从工厂二开始会更优(只有从第三个工厂到第四个工厂的道路在第二个时刻的金币足够多,而其他的道路的金币又足够少)。相信丧心病狂的出题人会构造的。

那么我们可以构造多个单调队列,单调队列qi维护所有假设这个机器人一直在走,那么第0个时刻在i的数,这样他们第k个时刻必定会走同一条道路。因为他在pos时刻开始走,所以当ipos>p或者有更优解时把它踢出队列。

最后再在取每一个单调队列的队首计算答案取max即可

上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1010,INF=1e16;
ll n,m,p,tzy,ans=-INF;
ll r[N][N],G[N];
ll sum[N][N],f[N][N];
ll w(ll wz){return ((wz%n+n)%n)==0?n:((wz%n+n)%n);}
ll val(ll st,ll la,ll ti){return sum[w(st+ti)][la+ti]-sum[st][la];}
ll head[N],tail[N];
struct jgt{ll gs,ks,pos;}q[N][N];
void init()
{
for(ll i=1;i<=m;i++)
for(ll j=1;j<=n;j++)
f[i][j]=-INF;
for(ll i=1;i<=n;i++)
f[0][i]=-G[i],head[i]=1;
}
int main()
{
scanf("%lld %lld %lld",&n,&m,&p);
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
scanf("%lld",&r[i][j]);
for(ll i=1;i<=n;i++)
scanf("%lld",&G[i]);
for(ll i=1;i<=m;i++)
for(ll j=1;j<=n;j++)
sum[j][i]=sum[w(j-1)][i-1]+r[w(j-1)][i];
init();
for(ll i=1;i<=m;i++)
{
for(ll st=1;st<=n;st++)
{
while(head[w(st-i+1)]<=tail[w(st-i+1)]&&q[w(st-i+1)][tail[w(st-i+1)]].gs+val(q[w(st-i+1)][tail[w(st-i+1)]].ks,q[w(st-i+1)][tail[w(st-i+1)]].pos,i-q[w(st-i+1)][tail[w(st-i+1)]].pos)<f[i-1][st]+val(st,i-1,1)) tail[w(st-i+1)]--;
q[w(st-i+1)][++tail[w(st-i+1)]]={f[i-1][st],st,i-1};
while(i-q[w(st-i+1)][head[w(st-i+1)]].pos>p) head[w(st-i+1)]++;
}
tzy=-INF;
for(ll j=1;j<=n;j++)
tzy=max(tzy,q[j][head[j]].gs+val(q[j][head[j]].ks,q[j][head[j]].pos,i-q[j][head[j]].pos));
for(ll j=1;j<=n;j++)
f[i][j]=tzy-G[j];
}
for(ll i=1;i<=n;i++)
ans=max(ans,f[m][i]+G[i]);
printf("%lld\n",ans);
return 0;
}
posted @   傻阙的缺  阅读(105)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示