归档 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
一开始被智力误导了,没有看到题目对于到达的诡异定义,以为这是道水题。
—— · EOF · ——
真的什么也不剩啦 😖