中山市选 #E 参数拟合

中山市选 #E 参数拟合

题目大意

有两个长度为 \(n\) 的序列 \(A,B\)。现在给出你 \(m\) 种操作,\((u,v)\) 表示你可以将 \(A_u,A_v\) 同时增加 \(q\) 为任意整数

最后需要使得 \(\sum (A_i-B_i)^2\) 最小。

解题思路

首先,我们令 \(C_i = A_i - B_i\) 这样我们的目标就是使得 \(\sum C_i^2\) 尽量小。

不难想到建一张图。

对于每一个连通块必然是单独考虑。

在一个连通块内,我们不妨以任意一个点为根,先跑出一棵树,规定父亲的深度为 \(1\)

对于树边,我们可以把儿子的 \(C_i\) 全部减到其父亲上。

这样我们可以计算出父亲节点最后剩余的 \(C_0\)

  • \(i\) 节点深度为奇数,则对 \(C_0\) 贡献 \(C_i\)
  • \(i\) 节点深度为偶数,则对 \(C_0\) 贡献 \(-C_i\)

接下来考虑非树边。

如果有非树边连接深度奇偶性不同的点 \(u,v\),我们不妨设根节点将 \(C_0\) 分给 \(u\) 方向(\(u\) 深度为奇数) \(a\)\(v\) 方向 \(b\)

那么 \(C_u = a,C_v = b\)

作差,发现 \(C_u = a - b\),由于 \(a + b = d\),且都为整数,故可以使得 \(C_u = d \mod 2\)

如果有非树边连接深度奇偶性相同的点,定义同上。

那么 \(C_u = a,C_v = -b\)

作差,发现 \(C_u = a + b = d\) 仍然无法解决问题。

\(D_i = |C_i|\),代价为 \(\sum D_i^2\) 我们可以在保证 \(\sum D_i = |C_0|\) 的情况下尽量均摊(整数),来使得代价最小,可以用柯西不等式简单证明。

计算过程详见代码中 Calc 函数。

参考代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6;
struct Edge{int u,v,nxt;};
int n,m;
int a[maxn+5];
int hd[maxn+5],et;
Edge e[maxn+5];
int de[maxn+5];
int tsum,tcnt;
bool flg;
int ans;
inline void Adde(int u,int v){
	e[et].u=u,e[et].v=v,e[et].nxt=hd[u],hd[u]=et++;
}
void Dfs(int u){
	int v;
	tcnt++;
	if(de[u]&1) tsum+=a[u];
	else tsum-=a[u];
	for(int i=hd[u];~i;i=e[i].nxt){
		v=e[i].v;
		if(de[v]){
			if((de[v]&1)==(de[u]&1)) flg=1;
		}
		else{
			de[v]=de[u]+1;
			Dfs(v);
		}
	}
}
inline int Calc(int sum,int cnt){
	int res,tmp;
	tmp=sum%cnt;
	res=sum/cnt;
	return tmp*(res+1)*(res+1)+(cnt-tmp)*res*res;
}
signed main(){
	int u,v,x;
	memset(hd,-1,sizeof(hd));
	memset(e,-1,sizeof(e));
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&x),a[i]-=x;
	while(m--) scanf("%lld%lld",&u,&v),Adde(u,v),Adde(v,u);
	for(int i=1;i<=n;i++){
		if(de[i]) continue;
		tsum=tcnt=0;
		flg=0;
		de[i]=1;
		Dfs(i);
		tsum=abs(tsum);
		if(flg) ans+=tsum&1;
		else ans+=Calc(tsum,tcnt);
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2024-07-07 09:04  DeepSeaSpray  阅读(10)  评论(0编辑  收藏  举报