最短路之和 解题报告

最短路之和

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

考虑这个\(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

posted @ 2019-03-23 22:32  露迭月  阅读(258)  评论(0编辑  收藏  举报