感觉非常高妙。
考虑暴力做法。
首先对于题目中的第三种限制:若两个环满足,那么这两个环拼起来得到的环肯定也满足。
那么我们可以只考虑那些互相独立的简单环。
随便找到原图的一棵生成树,那么一条非树边可以对应一个简单环,共 m − ( n − 1 ) m-(n-1) m−(n−1) 个,看成 m − ( n − 1 ) m-(n-1) m−(n−1) 条方程。
再配上第二条限制,总共就得到了 n + m − ( n − 1 ) = m + 1 n+m-(n-1)=m+1 n+m−(n−1)=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=0∑k−1B(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)xv−xu+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 BC−A 的和。那么对于树上的边 ( 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)xv−xu+A(u,v)。
使用了 m − ( n − 1 ) m-(n-1) m−(n−1) 条方程,消掉了 m − ( n − 1 ) 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;
}