[agc004F]Namori
Description
Solution
以下内容转载自大佬的博客,链接为https://blog.csdn.net/werkeytom_ftd/article/details/78393489。
----------------------------------------------------------------------------------------------------------
树的做法
树是一个二分图,看起来很棒的样子。
我们不妨设深度为奇数(根的深度为1)的点是一个空位,而深度为偶数的点有一个硬币。
我们发现,一次操作相当于将一个硬币移到相邻的空位。
最终要求原本是空位的点都被硬币填满。
那么只有硬币数等于空位数才有解。
不妨记空位为1,硬币为-1。
si表示子树和,最小的答案是∑ni=1abs(si)∑i=1nabs(si)
因为这个是一定要从这个子树运出的硬币数或运进的硬币数,这个值代表其和父亲边的操作次数。这个式子显然是下界。
而不难发现该下界可以达到。对于一个点,我们把它的儿子分为运进儿子和运出儿子,每次把运出儿子的一个硬币匹配到运进儿子,当然可行。
这个做法很重要,接下来我们来解决环套树,分两种情况讨论。
奇环
任意断开环上一边u和v,变成树。
u和v一定属于二分图同一侧,我们操作一次u-v,显然是在u和v各增加一个硬币或各减少一个硬币。
不妨计算出变成树后所多余或所缺少的硬币数量,如果是奇数则无解,否则均摊到u和v上。
然后像树的做法做一遍即可。
偶环
同样任意断开一边u和v。
然后设u-v这条边操作了x次(可以是负数),那么u上增加x个硬币,v上则减少x个硬币。
设ki表示子树i最终带x的系数,显然只有0、1、-1。
那么答案是abs(x)+∑ni=1abs(kix+si)abs(x)+∑i=1nabs(kix+si)
k为0的可以直接加进答案,剩下的可以看做数轴上若干点,选一个点使得这些点到该点距离和最小,是经典问题,取中位数最优。
----------------------------------------------------------------------------------------------------------
PS:这里的所谓硬币的定义,应该是当硬币在深度为偶数的点上,该点为白色;硬币在深度为奇数的点上,该点为黑色。应该是这个意思吧qaq
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,m,x,y; struct node{int y,nxt; }g[200010];int tot=0,h[100010]; bool vis[100010]; int cirx=0,ciry=0;bool is_single; int dep[100010],sum[100010],f[100010],ans=0; void dfs(int x,int fa) { f[x]=fa; vis[x]=1;dep[x]=dep[fa]^1;sum[x]=dep[x]&1?-1:1; for (int i=h[x];i;i=g[i].nxt) if (g[i].y!=fa){ if (vis[g[i].y]) { cirx=g[i].y;ciry=x;is_single=(dep[x]==dep[g[i].y]); } else dfs(g[i].y,x),sum[x]+=sum[g[i].y]; } ans+=abs(sum[x]); } int k[100010];//not single circle int p[100010],cntp; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { scanf("%d%d",&x,&y); g[++tot]=node{y,h[x]};h[x]=tot; g[++tot]=node{x,h[y]};h[y]=tot; } dfs(1,0); if (m==n) { if (is_single) { if (sum[1]&1) {printf("-1");return 0;} int t=-sum[1]/2; for (;cirx;cirx=f[cirx]) sum[cirx]+=t; for(;ciry;ciry=f[ciry]) sum[ciry]+=t; ans=abs(t); for (int i=1;i<=n;i++) ans+=abs(sum[i]); printf("%d",ans); } else { if (sum[1]){printf("-1");return 0;} for(;cirx;cirx=f[cirx]) k[cirx]++; for(;ciry;ciry=f[ciry]) k[ciry]--; p[cntp=1]=0;ans=0; for (int i=1;i<=n;i++) if (!k[i]) ans+=abs(sum[i]);else p[++cntp]=k[i]*sum[i]; sort(p+1,p+cntp+1); int t=p[1+cntp>>1]; for (int i=1;i<=cntp;i++) ans+=abs(p[i]-t); printf("%d",ans); } return 0; } if (!sum[1]) printf("%d",ans);else printf("-1"); }