中山市选 #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;
}