【XSY2416】带权图(图论,高斯消元)

感觉非常高妙。

考虑暴力做法。

首先对于题目中的第三种限制:若两个环满足,那么这两个环拼起来得到的环肯定也满足。

那么我们可以只考虑那些互相独立的简单环。

随便找到原图的一棵生成树,那么一条非树边可以对应一个简单环,共 \(m-(n-1)\) 个,看成 \(m-(n-1)\) 条方程。

再配上第二条限制,总共就得到了 \(n+m-(n-1)=m+1\) 条方程,共有 \(m\) 个未知数。

\(O(m^3)\) 高斯消元即可得到 60pts。

考虑先利用一些方程消去一些东西降低未知数个数。

\(path_u=v_0=1,v_1,\cdots,v_k=u\) 为任意一条从 \(1\)\(u\) 的路径,记 \(x_u=\sum\limits_{i=0}^{k-1}B(v_i,v_{i+1})C(v_i,v_{i+1})-A(v_i,v_{i+1})\)

利用第三条限制,可以证明无论 \(path_u\) 如何取,\(x_u\) 总是相同的。

那么对于任意一条边 \((u,v)\),就有:

\[C(u,v)=\dfrac{x_v-x_u+A(u,v)}{B(u,v)} \]

于是任意的 \(C(u,v)\) 都被表示成只和 \(x\) 有关的量。

\(x\) 的数量是 \(n\) 个,\(x_1=0\) 加上第二条限制共有 \(n+1\) 条方程,高斯消元即可,时间复杂度 \(O(n^3)\)

看起来很妙,理性分析一下我们是如何利用部分方程减少未知数个数的:

仍然考虑一开始的生成树做法,随便选一个点作为根,设 \(x_u\) 表示树上从根到 \(u\) 的简单路径经过的边的 \(BC-A\) 的和。那么对于树上的边 \((u,v)\) 我们就知道 \(C(u,v)\)\(x\) 表示的表达式。

对于一条非树边 \((u,v)\),暴力做法中它贡献了一条方程,根据这条方程我们可以推导出 \(C(u,v)=\dfrac{x_v-x_u+A(u,v)}{B(u,v)}\)

使用了 \(m-(n-1)\) 条方程,消掉了 \(m-(n-1)\) 个未知数,很合理。

反正这个表示方法还是很妙的。

#include<bits/stdc++.h>

#define N 110
#define M 2010
#define ll long long

using namespace std;

namespace modular
{
	ll mod;
	inline ll add(ll x,ll y){return x+y>=mod?x+y-mod:x+y;}
	inline ll dec(ll x,ll y){return x-y<0?x-y+mod:x-y;}
	inline ll mul(ll x,ll y){return (__int128)x*(__int128)y%mod;}
	inline void Add(ll &x,ll y){x=x+y>=mod?x+y-mod:x+y;}
	inline void Dec(ll &x,ll y){x=x-y<0?x-y+mod:x-y;}
	inline void Mul(ll &x,ll y){x=(__int128)x*(__int128)y%mod;}
}using namespace modular;

inline ll poww(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}

inline ll read()
{
	ll x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

struct Edge
{
	int u,v;
	ll a,b;
}e[M];

int n,m;
ll a[N][N],x[N];

void add(int u,int v,ll A,ll B)
{
	if(u==n) return;
	ll invB=poww(B,mod-2);
	Dec(a[u][u],invB);
	if(v!=n) Add(a[u][v],invB);
	Dec(a[u][n],mul(A,invB));
}

void Gauss(int n)
{
	for(int i=1;i<=n;i++)
	{
		int p=i;
		for(int j=i+1;j<=n;j++)
			if(a[j][i]) p=j;
		swap(a[i],a[p]);
		ll inv=poww(a[i][i],mod-2);
		for(int j=i+1;j<=n;j++)
		{
			ll tmp=mul(a[j][i],inv);
			for(int k=i;k<=n+1;k++)
				Dec(a[j][k],mul(a[i][k],tmp));
		}
	}
	for(int i=n;i>=1;i--)
	{
		for(int j=i+1;j<=n;j++)
			Dec(a[i][n+1],mul(a[i][j],x[j]));
		x[i]=mul(a[i][n+1],poww(a[i][i],mod-2));
	}
}

int main()
{
//	freopen("graph3.in","r",stdin);
//	freopen("graph3.out","w",stdout);
	n=read(),m=read(),mod=read();
	for(int i=1;i<=m;i++)
	{
		e[i].u=read(),e[i].v=read(),e[i].a=read(),e[i].b=read();
		add(e[i].u,e[i].v,e[i].a,e[i].b);
		add(e[i].v,e[i].u,dec(0,e[i].a),e[i].b);
	}
	Gauss(n-1);
	for(int i=1;i<=m;i++)
	{
		ll c=add(dec(x[e[i].v],x[e[i].u]),e[i].a);
		Mul(c,poww(e[i].b,mod-2));
		printf("%lld\n",c);
	}
	return 0;
}
posted @ 2022-10-30 10:08  ez_lcw  阅读(32)  评论(0编辑  收藏  举报