[NOIP2020] 排水系统 题解

[NOIP2020] 排水系统 题解


知识点

拓扑排序,高精度。

分析

首先这是一个明显的拓扑排序,用 long long 可以直接拿到 90 分(当年不能用 __int128),很多人到这里就直接上高精度了,但是注意到题目范围有两个小提示:

  1. \(0 \le d_i \le 5\)
  2. 数据保证,污水在从一个接收口流向一个最终排水口的过程中,不会经过超过 \(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;
}

posted @ 2024-11-13 13:36  plus_cat  阅读(3)  评论(0编辑  收藏  举报