cogimyunの小窝

Loading...

AT_yahoo_procon2019_qual_d Ears 题解

校内模拟赛上场切紫题 *800?


我们不妨先考虑从 \(u\) 出发直接走到 \(v\) 的贡献,此时对于 \(\forall i\in[u+1,v]\)\(i\) 堆石头数量都会加一。接下来我们考虑在从 \(u\) 走到 \(v\) 的过程中不是直接走到的,那么走法无非三种:

  1. 走到 \(u\) 的左侧然后走回
  2. 走到 \(v\) 的右侧然后走回
  3. 在一条路径上左右反复横跳

我们不难发现,无论是往 \(u\) 左走还是往 \(v\) 右走都必然要往回走,那么 \(u\) 左边与 \(v\) 右边走过的路径会经过偶数次,至于在一条路径上反复横跳不会影响路径经过次数的奇偶性,只是使一条路径的经过次数可以变为无限多次,于是我们不难发现我们最终的走法是一段未走的路径加一段走了偶数次的路径加一段走了奇数次的路径加一段走了偶数次的路径加一段未走的路径,其中每种路径都是可存在也可不存在的,于是我们就可以非常简单地动态规划,先处理出后缀路径走偶数次与不走的最优路径,然后从前往后扫一遍,一遍处理前缀路径走偶数次与不走的最优路径,和拼接一段前缀路径之后中间走奇数次的最优路径,然后将奇数次的最优路径拼接后缀路径更新答案即可。

CODE

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[200005],pre[200005][2],nxt[200005][2],dp[200005],ans=1e18;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    fill(dp,dp+n+3,1e17);
    for(int i=n;i>=1;i--){
        if(a[i]){
            if(a[i]%2)nxt[i][1]=min(nxt[i+1][1],nxt[i+1][0])+1;
            else nxt[i][1]=min(nxt[i+1][1],nxt[i+1][0]);
            nxt[i][0]=nxt[i+1][0]+a[i];
        }
        else{
            nxt[i][0]=nxt[i+1][0];
            nxt[i][1]=min(nxt[i+1][1],nxt[i+1][0])+2;
        }
    }
    for(int i=1;i<=n;i++){
        if(a[i]){
            if(a[i]%2)pre[i][1]=min(pre[i-1][1],pre[i-1][0])+1;
            else pre[i][1]=min(pre[i-1][1],pre[i-1][0]);
            pre[i][0]=pre[i-1][0]+a[i];
        }
        else{
            pre[i][0]=pre[i-1][0];
            pre[i][1]=min(pre[i-1][1],pre[i-1][0])+2;
        }
        ans=min(ans,min(nxt[i][1]+pre[i-1][0],nxt[i+1][0]+pre[i][1]));
        if(a[i]%2)dp[i]=min(dp[i-1],min(pre[i-1][0],pre[i-1][1]));
        else dp[i]=min(min(pre[i-1][0],pre[i-1][1]),dp[i-1])+1;
        ans=min(ans,dp[i]+min(nxt[i+1][0],nxt[i+1][1]));
    }
    ans=min(ans,pre[n][0]);
    cout<<ans;
    return 0;
}
posted @ 2025-10-30 18:15  cogimyun  阅读(6)  评论(0)    收藏  举报