最短路之和 解题报告
最短路之和
也许是一个常见套路,然而我并不会...
考虑这个\(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