#D.糖果镇

思路

  • m=3时整个路径有两个拐点,分别是m=1m=2,m=2m=3

  • 设拐点1在第i列,拐点2在第j列,则路径上的数字总和为(front[1][i])+(front[2][j]front[2][i1])+(back[j])(front[i][j]表示第i行1j的前缀和,back[j]表示第3jn的后缀和)

  • 把带有ij的项分开,设f[i]=(front[1][i]front[2][i1]),g[j]=(front[2][j]+ba[j]),则res=max1n(res,f[i]+g[j])

  • 所以只要枚举i,j就可以

数据点分治

m=1

  • 只能一直往前走,累加即可;
    //m=1
    if(m==1){
    ans=0;
    for(int i=1;i<=n;i++) ans=(ans+in[1][i])%p;
    cout<<ans%p<<"\n";
    return 0;
    }

m=2

  • 预处理f[i],g[i],暴力枚举一个拐点即可(两行只有一个拐点)
    //m=2
    else if(m==2){
    ans=0;
    for(int i=1;i<=n;i++)
    ans=max(ans,(front[1][i]+front[2][n]-front[2][i-1])%p);
    cout<<ans%p<<"\n";
    return 0;
    }

所有ai的和<模数p

  • 按照正常DP来做就可以
  • 为什么模数会对结果有影响:
    • 如果f[i]+g[j]恰好等于p+1f[x]+g[y]等于p1,那么同时模p后第一个的结果小于第二个,常规DP得到的却是第一个大于第二个,所以只有在取模对原结果无影响(原结果小于模数)时才能用常规DP
    //sum<p
    else if(sum<p){
    for(int i=1;i<=m;i++){
    for(int j=1;j<=n;j++){
    dp[i][j]=max(dp[i-1][j],dp[i][j-1])+in[i][j];
    }
    }
    cout<<dp[m][n]<<"\n";
    return 0;
    }

m=3

  • 按照上面的思路,f[i]ig[j]j,所以要按照一定顺序,把下标大于等于当前ig[i]找出来,和f[i]区和,求最大值

  • f[i],g[i]的时候一步一取模,使得f[i],g[i]p1,所以f[i]+g[j]>p<p两种情况,对于给定的f[i]

    • f[i]+g[j]>pg[i]属于[pf[i],p1]
    • f[i]+g[j]<pg[i]属于[0,p1f[i]]

值域线段树

  • 和普通线段树类似,不过改成了动态开点
  • 用值域线段树维护区间[0,p)中各个子区间内g[i]的最大值
  • 这里按照倒序(正序也可以)往线段树里插入g[i],再用前文分类讨论的两个区间区g[i]的最大值与f[i]想加,取最大值作为结果
    //线段树
    struct Node{
    int l,r;
    int lc,rc;
    int maxn;
    }t[40*N];
    int cnt=1;
    void Push_up(int id){
    t[id].maxn=-INF;
    if(t[id].lc) t[id].maxn=max(t[t[id].lc].maxn,t[id].maxn);
    if(t[id].rc) t[id].maxn=max(t[t[id].rc].maxn,t[id].maxn);
    }
    void Modify(int id,int pos){
    if(t[id].l==t[id].r){
    t[id].maxn=pos;
    return;
    }
    int mid=(t[id].l+t[id].r)/2;
    if(pos<=mid){
    if(!t[id].lc){
    t[id].lc=++cnt;
    t[cnt].l=t[id].l; t[cnt].r=mid;
    t[cnt].lc=0; t[cnt].rc=0;
    t[cnt].maxn=-INF;
    }
    Modify(t[id].lc,pos);
    }
    else{
    if(!t[id].rc){
    t[id].rc=++cnt;
    t[cnt].l=mid+1; t[cnt].r=t[id].r;
    t[cnt].lc=0; t[cnt].rc=0;
    t[cnt].maxn=-INF;
    }
    Modify(t[id].rc,pos);
    }
    Push_up(id);
    }
    int Query(int id,int l,int r){
    if(l<=t[id].l && t[id].r<=r) return t[id].maxn;
    int mid=(t[id].l+t[id].r)/2;
    int ret=-1;
    if(t[id].lc && l<=mid) ret=max(ret,Query(t[id].lc,l,r));
    if(t[id].rc && r>=mid+1) ret=max(ret,Query(t[id].rc,l,r));
    return ret;
    }
    //m=3
    ans=0;
    for(int i=n;i>=1;i--){
    Modify(1,g[i]);
    int ret;
    ret=Query(1,0,p-1-f[i]);
    if(ret!=-1) ans=max(ans,f[i]+ret);
    ret=Query(1,p-f[i],p-1);
    if(ret!=-1) ans=max(ans,(f[i]+ret)%p);
    }
    cout<<ans<<"\n";

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
const int N=1e5+10;
struct Node{
int l,r;
int lc,rc;
int maxn;
}t[40*N];
int cnt=1;
void Push_up(int id){
t[id].maxn=-INF;
if(t[id].lc) t[id].maxn=max(t[t[id].lc].maxn,t[id].maxn);
if(t[id].rc) t[id].maxn=max(t[t[id].rc].maxn,t[id].maxn);
}
void Modify(int id,int pos){
if(t[id].l==t[id].r){
t[id].maxn=pos;
return;
}
int mid=(t[id].l+t[id].r)/2;
if(pos<=mid){
if(!t[id].lc){
t[id].lc=++cnt;
t[cnt].l=t[id].l; t[cnt].r=mid;
t[cnt].lc=0; t[cnt].rc=0;
t[cnt].maxn=-INF;
}
Modify(t[id].lc,pos);
}
else{
if(!t[id].rc){
t[id].rc=++cnt;
t[cnt].l=mid+1; t[cnt].r=t[id].r;
t[cnt].lc=0; t[cnt].rc=0;
t[cnt].maxn=-INF;
}
Modify(t[id].rc,pos);
}
Push_up(id);
}
int Query(int id,int l,int r){
if(l<=t[id].l && t[id].r<=r) return t[id].maxn;
int mid=(t[id].l+t[id].r)/2;
int ret=-1;
if(t[id].lc && l<=mid) ret=max(ret,Query(t[id].lc,l,r));
if(t[id].rc && r>=mid+1) ret=max(ret,Query(t[id].rc,l,r));
return ret;
}
int n,m,p;
int ans,sum;
int in[4][N];
int dp[4][N];
int front[3][N],back[N];
int f[N],g[N];
signed main(){
// freopen("1.in","r",stdin);
cin>>n>>m>>p;
t[1].maxn=-INF; t[1].l=0; t[1].r=p; t[1].lc=0; t[1].rc=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
cin>>in[i][j];
sum+=in[i][j];
}
}
for(int i=1;i<=2;i++){
for(int j=1;j<=n;j++){
front[i][j]=front[i][j-1]+in[i][j];
}
}
if(m==1){
ans=0;
for(int i=1;i<=n;i++) ans=(ans+in[1][i])%p;
cout<<ans%p<<"\n";
return 0;
}else if(m==2){
ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,(front[1][i]+front[2][n]-front[2][i-1])%p);
cout<<ans%p<<"\n";
return 0;
}else if(sum<p){
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+in[i][j];
}
}
cout<<dp[m][n]<<"\n";
return 0;
}
for(int i=n;i>=1;i--){
back[i]=back[i+1]+in[3][i];
}
for(int i=1;i<=n;i++){
f[i]=(front[1][i]-front[2][i-1])%p;
g[i]=(front[2][i]+back[i])%p;
}
ans=0;
for(int i=n;i>=1;i--){
Modify(1,g[i]);
int ret;
ret=Query(1,0,p-1-f[i]);
if(ret!=-1) ans=max(ans,f[i]+ret);
ret=Query(1,p-f[i],p-1);
if(ret!=-1) ans=max(ans,(f[i]+ret)%p);
}
cout<<ans<<"\n";
}
posted on   Bubble_e  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话



点击右上角即可分享
微信分享提示