最短路之和 解题报告

最短路之和

也许是一个常见套路,然而我并不会...

考虑这个\(2 \times n\)的格子形成的树的结构

任意相邻的两列都有一条边或者两条边,称两条边的相邻两列为联通,联通具有传递性。

性质:在一个联通列中,有且仅有一条竖边

于是可以按联通性进行划分进而dp

比如这个图就划分出了5个联通块

对于这个题,首先按边统计贡献,每个边贡献为\(sum(sum-x)\)\(sum\)是总点权,\(x\)是这个边任意一侧的点权和。

然后做dp就好了,\(dp_{i,0/1}\)表示\(i\)\(i+1\)列只连一条边的最小价值
\[ dp_{n,br}=\min_{m,bl} (dp_{m-1,bl}+cost(m,n,bl,br)) \]
后面的\(cost\)随便\(O(n)\)算一算就好了


Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <queue>
#include <algorithm>
#include <vector>
#define ll long long
using std::min;
const int N=52;
template <class T>
void read(T &x)
{
    x=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
ll a[2][N],fl[N],fr[N],sum,dp[N][2];
ll cost(int n,int br,int m,int bl)
{
    if(n==m)
    {
        a[bl][m]+=fl[m-1];
        a[br][n]+=fr[n+1];
        ll mi=a[bl][m]*(sum-a[bl][m]);
        a[bl][m]-=fl[m-1];
        a[br][n]-=fr[n+1];
        return mi;
    }
    a[bl][m]+=fl[m-1];
    a[br][n]+=fr[n+1];

    ll sup=0,sdow=0,yuy=0,sumdow=0,mi=1e18;
    for(int i=n;i>m;i--)
    {
        sup+=a[0][i];
        sdow+=a[1][i];
        yuy+=sup*(sum-sup);
        yuy+=sdow*(sum-sdow);
        sumdow+=a[1][i];
    }
    sumdow+=a[1][m];
    sdow+=a[1][m];
    sup+=a[0][m];
    ll dl=0;
    for(int i=m;i<=n;i++)
    {
        yuy+=sumdow*(sum-sumdow);
        mi=min(yuy,mi);
        yuy-=sumdow*(sum-sumdow);

        sdow-=a[1][i];
        sup-=a[0][i];

        yuy-=sdow*(sum-sdow);
        dl+=a[1][i];
        yuy+=dl*(sum-dl);

        yuy-=sup*(sum-sup);
        yuy+=(sup+sumdow)*(sum-sup-sumdow);
    }

    a[bl][m]-=fl[m-1];
    a[br][n]-=fr[n+1];
    return mi;
}
void work()
{
    int n;
    read(n);sum=0;ll su=0;
    for(int i=1;i<=n;i++) read(a[0][i]),sum+=a[0][i];
    for(int i=1;i<=n;i++) read(a[1][i]),sum+=a[1][i];
    fr[n+1]=0;
    for(int i=n;i;i--) fr[i]=fr[i+1]+a[0][i]+a[1][i];
    for(int i=1;i<=n;i++) fl[i]=fl[i-1]+a[0][i]+a[1][i];
    memset(dp,0x3f,sizeof dp);
    dp[0][0]=dp[0][1]=0;
    for(int i=1;i<=n;i++)
    {
        su+=a[0][i]+a[1][i];
        for(int j=1;j<=i;j++)
        {
            dp[i][0]=min(dp[i][0],dp[j-1][0]+cost(i,0,j,0));
            dp[i][0]=min(dp[i][0],dp[j-1][1]+cost(i,0,j,1));
            dp[i][1]=min(dp[i][1],dp[j-1][0]+cost(i,1,j,0));
            dp[i][1]=min(dp[i][1],dp[j-1][1]+cost(i,1,j,1));
        }
        dp[i][0]+=su*(sum-su);
        dp[i][1]+=su*(sum-su);
    }
    printf("%lld\n",dp[n][0]);
}
int main()
{
    //freopen("sum.in","r",stdin);
    //freopen("sum.out","w",stdout);
    int T;read(T);
    while(T--) work();
    return 0;
}

2019.3.23

猜你喜欢

转载自www.cnblogs.com/butterflydew/p/10586028.html
今日推荐