【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)\) 都被表示成只和 \(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;
}