中山市选 #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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步