[数学][构造]JZOJ 3317 管道
分析
设e[u,v]为连接u,v的边的值的一半
则有:$c[u]=\Sigma e[u,v]$这么多个方程组
m=n-1就是棵树,树只要从叶子节点推上来就一定有唯一解
那么m>n时,变量数大于方程数,定然无解
重点讨论m=n
m=n一定是一个环伸出多条边,把边的答案计算完,留下计算过后的环的v
很明显,对于一个点$c[u]=e(u,v1)+e(v2,u)$
不好解,我们考虑消去变量:e(v2,u)+e(u,v1)-e(u,v1)-e(v1,v3)+e(v1,v3)+e(v3,v4)……=c[u]-c[v1]+c[v3]-……
这样构造到最后,显然$ans=2*e(v2,u)=\Sigma c[u]*(-1)^dep$
#include <iostream> #include <cstdio> #include <queue> using namespace std; typedef long long ll; const int N=1e5+10; struct Graph { int v,nx,id; }g[2*N]; int cnt,list[N],c[N],ans[N],deg[N]; bool b[2*N]; int n,m,p; void Add(int u,int v,int id) { g[++cnt]=(Graph){v,list[u],id};list[u]=cnt; g[++cnt]=(Graph){u,list[v],id};list[v]=cnt; } void Topposort() { queue<int> q; while (!q.empty()) q.pop(); for (int i=1;i<=n;i++) if (deg[i]==1) q.push(i),p--; while (!q.empty()) { int u=q.front();q.pop(); for (int i=list[u];i;i=g[i].nx) if (deg[g[i].v]) { if (--deg[g[i].v]==1) q.push(g[i].v),p--; ans[g[i].id]=2*c[u]; c[g[i].v]-=c[u];c[u]=0; deg[u]--; } } } void Pre(int u,int fa) { for (int i=list[u];i;i=g[i].nx) if (deg[g[i].v]&&g[i].v!=fa&&!b[i]) { b[i]=1; Pre(g[i].v,u); return; } } void DFS(int u,int fa,int I,int dep) { ans[g[I].id]+=((dep&1)?c[u]:-1*c[u]); for (int i=list[u];i;i=g[i].nx) if (b[i]) { if (i==I) return; DFS(g[i].v,u,!I?i:I,dep+1); return; } } void Calc(int u,int fa,int from,int dep) { for (int i=list[u];i;i=g[i].nx) if (b[i]) { if (dep==p) return; if (fa) ans[g[i].id]=-(ans[g[from].id]-c[u])+c[u]; Calc(g[i].v,u,i,dep+1); return; } } int main() { scanf("%d%d",&n,&m); if (m>n) { printf("0"); return 0; } for (int i=1;i<=n;i++) scanf("%d",&c[i]); for (int i=1,u,v;i<=m;i++) { scanf("%d%d",&u,&v); deg[u]++;deg[v]++; Add(u,v,i); } p=n; Topposort(); if (m==n-1) { for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; } if (!(p%2)) { printf("0"); return 0; } for (int i=1;i<=n;i++) if (deg[i]) { Pre(i,0); break; } for (int i=1;i<=n;i++) if (deg[i]) { DFS(i,0,0,0); Calc(i,0,0,0); break; } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); }
在日渐沉没的世界里,我发现了你。