D154 拓扑排序 P7113 [NOIP2020] 排水系统
D154 拓扑排序 P7113 [NOIP2020] 排水系统_哔哩哔哩_bilibili
有向无环图,m 个入口结点,n 个非入口结点,每个入口灌入单位 1 的水,求每个出口的出水量 $p/q$
$1\le n\le 10^5,1\le m\le 10,0\le d_i\le 5$

思路
有向无环图,跑拓扑排序,累计结点的出水量
怎样累计从 u 点流到 v 点的水量?
用 u 点的水量 $\frac{u_1}{u_2}$ 乘以 u 点的出度分之一,累计到 v 点已有的水量 $\frac{v_1}{v_2}$
要先通分,算出分子、分母,再求两者的最大公约数,化简分数

分母的最大值怎样估算?
考虑三条出度分别为 5、4、3(因为它们互质)的“毛刷链”,累计水量的分母分别为 $5^{10},4^{10},3^{10}$,通分的乘积为 $6\times 10^{17}$
再考虑同样的三条“毛刷链”,通分的乘积为 $6\times 10^{17}$,这六条“毛刷链”通分的乘积约为 $3\times 10^{35}$
64 位的 $long\; long$ 型,约为 $1\times 10^{19}$;128 位的 $\_\_128int$ 型,约为 $3\times 10^{38}$,所以用 $\_\_128int$ 或高精度

相关板子:
// 拓扑排序 O(n) #include<bits/stdc++.h> #define i8 __int128 using namespace std; int read(){ int x=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x; } const int N=100005; int h[N],idx,ne[N*5],to[N*5]; void add(int u,int v){ to[++idx]=v;ne[idx]=h[u];h[u]=idx; } int n,m,cd[N],rd[N]; //出度,入度 i8 fz[N],fm[N]; //分子,分母 i8 gcd(i8 x,i8 y){ //求最大公约数 i8 t; while(y){ t=y; y=x%y; x=t; } return x; } void calc(i8 &v1,i8 &v2,i8 u1,i8 u2){ //求v1/v2+u1/u2 v1=v1*u2+v2*u1; v2=v2*u2; i8 g=gcd(v1,v2); v1/=g; v2/=g; } void topo(){ //拓扑排序 queue<int> q; for(int i=1;i<=m;++i){ fz[i]=1; //分子初值 q.push(i); //m个入口入队 } while(!q.empty()){ int u=q.front(); q.pop(); for(int i=h[u];i;i=ne[i]){ int v=to[i]; calc(fz[v],fm[v],fz[u],fm[u]*cd[u]); if(--rd[v]==0) q.push(v); } } } void write(i8 x){ //大数字输出 if(x>9) write(x/10); putchar(x%10+'0'); } int main(){ n=read(),m=read(); for(int i=1,d;i<=n;++i){ d=read(); fm[i]=1; //分母初值 cd[i]+=d; //出度 for(int j=1,x;j<=d;++j){ x=read(); add(i,x); rd[x]++; //入度 } } topo(); for(int i=1;i<=n;++i)if(!cd[i]){ //枚举出口 write(fz[i]); putchar(' '); write(fm[i]); putchar('\n'); } return 0; }
浙公网安备 33010602011771号