归档 230418 // 补题

凳子充分地让我切身实地地体会到了平行四边形的不稳定性。做题的时候可以 360° 无死角地把下半身转来转去,有点意思。也有点容易摔(×

A. 两双手

http://222.180.160.110:1024/contest/3506/problem/1

不难发现可以通过解二元一次方程组得到用几个 A 操作和 B 操作才能让横纵坐标分别改变一些特定的值。可惜我没能想到一个经典的容斥 DP 思想:

定义起点为点 \(0\)。定义 \(C(x,y)\) 表示从 \(x\) 点走到 \(y\) 点的方案数(可经过特殊点)。对于特殊点 \(1\sim n\),顺序枚举,对于点 \(i\),定义 \(f_i\) 表示从 \(0\sim i\) 不经过任何除 \(i\) 以外的特殊点的方案数,则 \(f_i=C(0,i)-\sum\limits_{j=1}^{i-1}f_j\times C(j,i)\)。其中 \(\sum\limits_{j=1}^{i-1}f_j\times C(j,i)\) 就是从 \(0\)\(i\),除 \(i\) 以外经过至少 1 个特殊点的方案数。用总方案数减去不合法方案数,则得到合法方案数。

一个细节。我们枚举时,可能会遇到只能从 \(i\) 到达 \(j\),而不能从 \(j\) 到达 \(i\) 的情况。若恰好 \(j\)\(i\) 前面,那么从 \(i\)\(j\) 的路径就会被统计漏。这个时候怎么办呢?对于任意点 \(i\),我们求得从 \(0\to i\) 所需的步数,并以此为关键字从小到大排序,那么我们就能保证,任意一条合法路径都会被我们统计到。

还是没那么难,有点套路。时间复杂度 \(\mathcal O(n^2)\)

namespace XSC062 {
using namespace fastIO;
#define mkp std::make_pair
using pii = std::pair<int, int>;
const int lim = 1e6;
const int maxn = 505;
const int mod = 1e9 + 7;
const int maxm = 1e6 + 5;
struct _ { int x, y, w; };
std::set<pii> t;
_ s[maxn], s1[maxn];
int fac[maxm], f[maxn];
int ex, ey, n1, n, ax, ay, bx, by;
inline int gcd(int x, int y) {
	return y ? gcd(y, x % y) : x;
}
inline int lcm(int x, int y) {
	return x / gcd(x, y) * y;
}
pii Solve(int a, int b, int c,
				int d, int e, int f) {
	int N = 0, M = 0;
	if (!a) {
		if (c % b)
			return mkp(-1, -1);
		M = c / b, f -= M * e;
		if (f % d)
			return mkp(-1, -1);
		N = f / d;
	}
	else if (!b) {
		if (c % a)
			return mkp(-1, -1);
		N = c / a, f -= N * d;
		if (f % e)
			return mkp(-1, -1);
		M = f / e;
	}
	else if (!d) {
		if (f % e)
			return mkp(-1, -1);
		M = f / e, f -= M * b;
		if (c % a)
			return mkp(-1, -1);
		N = c / a;
	}
	else if (!e) {
		if (f % d)
			return mkp(-1, -1);
		N = f / d, c -= N * a;
		if (c % b)
			return mkp(-1, -1);
		M = c / b;
	}
	else {
		int g = lcm(b, e);
		a *= g / b, c *= g / b, b = g;
		d *= g / e, f *= g / e, e = g;
		if ((c - f) % (a - d))
			return mkp(-1, -1);
		N = (c - f) / (a - d), c -= N * a;
		if (c % b)
			return mkp(-1, -1);
		M = c / b;
	}
	return mkp(N, M);
}
inline int qkp(int x, int y) {
	int res = 1;
	while (y) {
		if (y & 1)
			(res *= x) %= mod;
		(x *= x) %= mod;
		y >>= 1;
	}
	return res;
}
inline int inv(int x) {
	return qkp(x, mod - 2);
}
inline int A(int n, int m) {
	return (fac[n] * inv(fac[n - m])) % mod;
}
inline int C(int n, int m) {
	return (A(n, m) * inv(A(m, m))) % mod;
}
inline void Init(void) {
	fac[0] = 1;
	for (int i = 1; i <= lim; ++i)
		fac[i] = (fac[i - 1] * i) % mod;
	return;
}
int main() {
	read(ex), read(ey), read(n1);
	read(ax), read(ay), read(bx), read(by);
	for (int i = 1; i <= n1; ++i) {
		read(s1[i].x), read(s1[i].y);
		pii t = Solve(ax, bx, s1[i].x,
						ay, by, s1[i].y);
		s1[i].w = t.first + t.second;
	}
	std::sort(s1 + 1, s1 + n1 + 1,
		[&](_ x, _ y) { return x.w < y.w; });
	Init(), f[0] = 1, ++n1;
	s1[n1].x = ex, s1[n1].y = ey;
	for (int i = 1; i <= n1; ++i) {
		pii t = Solve(ax, bx, s1[i].x,
						ay, by, s1[i].y);
		if (t.first < 0 || t.second < 0) {
			if (i == n1) {
				puts("0");
				return 0;
			}
			continue;
		}
		s[++n] = s1[i];
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j < i; ++j) {
			pii t = Solve(ax, bx, s[i].x
				- s[j].x, ay, by, s[i].y
				- s[j].y);
			if (t.first < 0 || t.second < 0)
				continue;
			(f[i] += (f[j] * C(t.first +
					t.second, t.first)) %
					mod) %= mod;
		}
		pii t = Solve(ax, bx, s[i].x,
					ay, by, s[i].y);
		f[i] = (C(t.first + t.second,
				t.first) + mod - f[i]) % mod;
	}
	print(f[n]);
	return 0;
}
} // namespace XSC062

B. 绮丽的天空

http://222.180.160.110:1024/contest/3506/problem/2

初看,哎呀,这不就是一个简单的缩点 + 拓扑吗?为什么没人做呢?这道题一定有鬼!

好吧,这题确实水…… 注意拓扑时父节点信息的转移。

namespace XSC062 {
#define mkp std::make_pair
using namespace fastIO;
using pii = std::pair<int, int>;
const int maxn = 5e5 + 5;
std::set<pii> s;
std::stack<int> t;
std::queue<int> q;
int a[maxn], f[maxn];
int n, m, x, y, now, cntv, res; 
int mx[maxn], su[maxn], deg[maxn];
std::vector<int> g[maxn], g1[maxn];
int low[maxn], dfn[maxn], scc[maxn];
inline int min(int x, int y) {
	return x < y ? x : y;
} 
inline int max(int x, int y) {
	return x > y ? x : y;
} 
void DFS(int x) {
	t.push(x);
	dfn[x] = low[x] = ++now;
	for (auto i : g[x]) {
		if (!dfn[i]) {
			DFS(i);
			low[x] = min(low[x], low[i]);
		}
		else if (!scc[i])
			low[x] = min(low[x], dfn[i]);
	}
	if (dfn[x] == low[x]) {
		++cntv;
		int p;
		do {
			p = t.top();
			t.pop();
			scc[p] = cntv;
			mx[cntv] = max(mx[cntv], a[p]);
			su[cntv] += a[p];
		} while (p != x);
	}
	return;
}
inline void add(int x, int y) {
	g[x].push_back(y);
	return;
}
inline void add1(int x, int y) {
	g1[x].push_back(y);
	return;
}
inline void Topo(void) {
	for (int i = 1; i <= cntv; ++i) {
		if (!deg[i])
			q.push(i);
	}
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		su[x] += su[f[x]];
		mx[x] = max(mx[x], mx[f[x]]);
		for (auto i : g1[x]) {
			if (su[x] > su[f[i]])
				f[i] = x;
			else if (su[x] == su[f[i]] &&
						mx[x] > mx[f[i]])
				f[i] = x;
			if ((--deg[i]) == 0)
				q.push(i);
		}
	}
	return;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; ++i)
		read(a[i]);
	while (m--) {
		read(x), read(y);
		add(x, y);
	}
	for (int i = 1; i <= n; ++i) {
		if (!dfn[i])
			DFS(i);
	}
	for (int i = 1; i <= n; ++i) {
		for (auto j : g[i]) {
			if (scc[i] == scc[j])
				continue;
			if (s.find(mkp(scc[i],
					scc[j])) != s.end())
				continue;
			s.insert(mkp(scc[i], scc[j]));
			add1(scc[i], scc[j]);
			++deg[scc[j]];
		}
	}
	Topo();
	for (int i = 1; i <= n; ++i) {
		if (su[i] > su[res])
			res = i;
		else if (su[i] == su[res] &&
					mx[i] > mx[res])
			res = i;
	}
	print(su[res], ' '), print(mx[res]);
	return 0;
}
} // namespace XSC062

C. Network

http://222.180.160.110:1024/contest/3506/problem/3

一开始被智力误导了,没有看到题目对于到达的诡异定义,以为这是道水题。

posted @ 2023-04-18 16:48  XSC062  阅读(29)  评论(5编辑  收藏  举报