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

感觉非常高妙。

考虑暴力做法。

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

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

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

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

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

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

p a t h u = v 0 = 1 , v 1 , ⋯   , v k = u path_u=v_0=1,v_1,\cdots,v_k=u pathu=v0=1,v1,,vk=u 为任意一条从 1 1 1 u u u 的路径,记 x u = ∑ i = 0 k − 1 B ( v i , v i + 1 ) C ( v i , v i + 1 ) − A ( v i , v i + 1 ) 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}) xu=i=0k1B(vi,vi+1)C(vi,vi+1)A(vi,vi+1)

利用第三条限制,可以证明无论 p a t h u path_u pathu 如何取, x u x_u xu 总是相同的。

那么对于任意一条边 ( u , v ) (u,v) (u,v),就有:
C ( u , v ) = x v − x u + A ( u , v ) B ( u , v ) C(u,v)=\dfrac{x_v-x_u+A(u,v)}{B(u,v)} C(u,v)=B(u,v)xvxu+A(u,v)
于是任意的 C ( u , v ) C(u,v) C(u,v) 都被表示成只和 x x x 有关的量。

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

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

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

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

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

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

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值