![]()

星际穿越:20XX年,11区发明了一种革命性的星际运输技术。其细节如下——
首先空间被降维为二维:
物质在原点(1,1)处被分解为单位质点,每个质点被给予不同的能量波。能量波是由VH两种信号构成的序列,V代表横坐标加一,H代表纵坐标加一。
然后,宇宙中存在多个虫洞,会影响质点的运动轨迹。
当物质进入虫洞入口时,会瞬间被传送到该虫洞的出口。第i个虫洞的坐标是(ai, bi), (ci, di),且满足ai ≤ ci, bi ≤ di, (ai, bi) ≠ (ci, di)。
最后,同时到达终点的质点将被组合为原物质。
你是宇航局的首席程序员,请计算在K个虫洞的空间中,往(N, M)最多能够同时运送多大质量的物质?答案数量级可能很大,请对1,000,000,007求余。
4.1更加复杂的数学问题
计数
终于逃离Burnside的魔爪了
,但还是离不开题解
题目其实是求到达某一点的所有方案数,如果不考虑虫洞的话就相当简单,答案是组合数CN+MN。
设起点为s,质点只能往右上走,所以——

当某个点p左下有虫洞的起点b的时候,到达它的路径数就减少s->b->p。
当某个点p左下有虫洞的终点e的时候,到达它的路径数就增加s->b->e->p。
于是定义dp[i] := 到第i个虫洞入口时的方案数,将虫洞排序后顺序递推即可(此时由于按b递增,s与b之间没有虫洞的干扰,就可以直接用组合数算了)。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define MOD 1000000007
inline int add(int a, int b)
{ return (a + b) % MOD; }
inline int sub(int a, int b)
{ return (a - b + MOD) % MOD; }
inline int mul(int a, int b)
{ return 1LL * a * b % MOD; }
// 虫洞
struct warp
{
int x1, y1, x2, y2;
bool operator<(const warp &w) const
{
if (x1 != w.x1)
return x1 < w.x1;
return y1 < w.y1;
}
};
// return d = gcd(a, b) = ax + by
int extgcd(int a, int b, int &x, int &y)
{
int g = a;
x = 1;
y = 0;
if (b != 0)
g = extgcd(b, a % b, y, x), y -= a / b * x;
return g;
}
// 求a的逆元
int inv(int a)
{
int x, y;
extgcd(a, MOD, x, y);
return (MOD + x % MOD) % MOD;
}
int fact[200010]; // 阶乘n!
void init_factorial()
{
fact[0] = 1;
for (int i = 0; i < 200000; ++i)
fact[i + 1] = mul(fact[i], i + 1);
}
// 组合数(从n个序列空位中挑出k个)
inline int nck(int n, int k)
{ return mul(mul(fact[n], inv(fact[n - k])), inv(fact[k])); }
int N, M, K, dp[100010];
// dp[i] := 到第i个虫洞入口时的方案数
vector<warp> warps;
inline int calc(int x1, int y1, int x2, int y2) // 无虫洞模式下s到t的方案数
{
if (x2 < x1 or y2 < y1)
return 0;
return nck(x2 - x1 + y2 - y1, x2 - x1);
}
int solve()
{
memset(dp, 0, sizeof(dp));
sort(begin(warps), end(warps));
warps.push_back((warp) {N, M, N + 1, M + 1});
for (int i = 0; i <= K; ++i)
{
dp[i] = nck(warps[i].x1 + warps[i].y1, warps[i].y1);
for (int j = 0; j < i; ++j)
{
dp[i] = add(dp[i], mul(dp[j], sub(calc(warps[j].x2, warps[j].y2, warps[i].x1, warps[i].y1), // j的终点到i的起点(紫色)
calc(warps[j].x1, warps[j].y1, warps[i].x1, warps[i].y1)))); // j的起点到i的起点
}
}
return dp[K];
}
bool input()
{
warps.clear();
scanf("%d%d%d", &N, &M, &K);
--N;
--M;
int a, b, c, d;
for (int i = 0; i < K; ++i)
{
scanf("%d%d%d%d", &a, &b, &c, &d);
warps.push_back((warp) {a - 1, b - 1, c - 1, d - 1});
}
return N + 1 or M + 1 or K;
}
int main()
{
init_factorial();
while (input())
cout << solve() << endl;
return 0;
}

解决AOJ无法登陆
近期AOJ无法登陆,症状为点击login无反应。打开控制台一看原来是引用了Google和Twitter的js库:

解决方案只有科学上网,呵呵,终于发展到连刷题都要借助梯子的地步了
Reference
http://algoogle.hadrori.jp/aoj/2214/
码农场

唉,完全看不懂。