[NOIP2020] 排水系统 题解
[NOIP2020] 排水系统 题解
知识点
拓扑排序,高精度。
分析
首先这是一个明显的拓扑排序,用 long long
可以直接拿到 90 分(当年不能用 __int128
),很多人到这里就直接上高精度了,但是注意到题目范围有两个小提示:
- \(0 \le d_i \le 5\);
- 数据保证,污水在从一个接收口流向一个最终排水口的过程中,不会经过超过 \(10\) 个中间排水结点(即接收口和最终排水口不算在内)。
那么我们可以得知,最终的分母分子只有 \(2,3,5\) 三个质因子,并且大小不超过 \(2^{22}\times 3^{11}\times 5^{11}\),那么我们直接设所有的分母都为 \(2^{22}\times 3^{11}\times 5^{11}\),然后用两个 unsigned long long
拼成一个简易的高精度,最后输出的时候试除 \(2,3,5\) 即可。
时间复杂度 \(O(n)\),常数略大。
启示
注意题目的数据范围可能可以大大减小代码难度。
代码
#define Plus_Cat "water"
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define FOR(i,a,b) for(int i(a);i<=(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(b);--i)
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i(g.h[x]),y(g[i].v);~i;y=g[i=g[i].nxt].v)
using namespace std;
namespace IOstream {
#define getc() getchar()
#define putc(c) putchar(c)
#define isdigit(c) ('0'<=(c)&&(c)<='9')
#define blank(c) (((c)==' '||(c)=='\n'||(c)=='\r'||(c)==(EOF)))
template<class T>void rd(T &x) {
static bool sign(0);
static char ch(0);
for(sign=0,ch=getc(),x=0; !isdigit(ch); ch=getc())if(ch=='-')sign=1;
for(; isdigit(ch); x=(x<<1)+(x<<3)+(ch^'0'),ch=getc());
return x=sign?-x:x,void();
}
template<class T>void wr(const T x,const char End='\n') {
T y(x);
static short top(0);
static short dig[100];
do dig[++top]=y%10,y/=10;
while(y);
while(top)putc(dig[top]^'0'),--top;
return putc(End),void();
}
#undef blank
} using namespace IOstream;
constexpr int N(1e5+10);
constexpr ull P(1e17);
bool mark[N];
int n,m;
int deg[N];
vector<int> g[N];
struct Int {
ull a[2];
ull &operator[](bool i) {
return a[i];
}
Int(ull x=0) {
a[0]=x,a[1]=0;
}
Int(ull x0,ull x1) {
a[0]=x0,a[1]=x1;
}
Int(const Int &x) {
*this=x;
}
Int &operator =(const Int &x) {
return this->a[0]=x.a[0],this->a[1]=x.a[1],*this;
}
friend Int operator +(Int A,Int B) {
return Int((A[0]+B[0])%P,A[1]+B[1]+(A[0]+B[0])/P);
}
friend Int operator /(Int A,ull b) {
return Int((A[0]+A[1]%b*P)/b,A[1]/b);
}
friend ull operator %(Int A,ull b) {
if(b==2)return A[0]&1;
if(b==3)return (A[0]%3+A[1]%3)%3;
return A[0]%5;
}
bool zero() {
return !a[0]&&!a[1];
}
void Init() {
a[0]=79705600000000000ull,a[1]=362ull;
}
void Print(const char End='\n') {
if(a[1])return printf("%llu%017llu",a[1],a[0]),putchar(End),void();
return printf("%llu",a[0]),putchar(End),void();
}
} f[N];
void Div(Int A) {
Int B;
B.Init();
while(!A.zero()&&!B.zero()&&!(A%2ull)&&!(B%2ull))A=A/2,B=B/2;
while(!A.zero()&&!B.zero()&&!(A%3ull)&&!(B%3ull))A=A/3,B=B/3;
while(!A.zero()&&!B.zero()&&!(A%5ull)&&!(B%5ull))A=A/5,B=B/5;
A.Print(' '),B.Print();
}
void Topo() {
queue<int> q;
FOR(i,1,m)f[i].Init();
FOR(i,1,n)if(!deg[i])q.push(i);
while(!q.empty()) {
int u(q.front());
q.pop();
for(int v:g[u]) {
f[v]=f[v]+(f[u]/g[u].size());
if(!--deg[v])q.push(v);
}
}
}
int main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
rd(n),rd(m);
FOR(u,1,n) {
int k,v;
rd(k);
FOR(i,1,k)rd(v),g[u].push_back(v),++deg[v];
if(!k)mark[u]=1;
}
Topo();
FOR(u,1,n)if(mark[u])Div(f[u]);
return 0;
}