我想在图中找到长度为N的路径数,其中顶点可以是任何自然数。但是,只有当两个顶点的乘积小于某个自然数P时,才连接两个顶点。如果两个顶点的乘积大于P,那么它们之间没有连接,并且不能相互到达。
我显然可以运行两个嵌套循环(< = P)并创建一个邻接矩阵,但P可能非常大,这种方法会非常慢。谁能想到一些解决问题的最佳方法?我们可以使用动态编程解决它吗?
答案 0 :(得分:2)
我同意Ante的复发,尽管我使用的是略微简化的版本。请注意,我使用字母P来命名最大产品,因为它在the original problem statement中使用:
f(1,x) = 1
f(i,x) = sum(f(i-1, y) for y in {1, ..., floor(P/x)})
f(i,x)是以 x 结尾的长度 i 的序列数。这个问题的答案是 f(n + 1,1)。
当然,由于在此任务中P可以达到10 ^ 9,因此使用DP表的直接实现是不可能的。但是,只有m <1。可能有70000个不同的 floor(P / i)值。所以让我们找到最大的段 a j ... b j ,其中 floor(P / a j )= floor(P / b j )。我们可以使用二进制搜索在 O(段数* log P)中找到这些段。
想象一下 f 的完整DP表。由于 floor(P / x)只有 m 不同的值, f 的每一行都包含 m 具有相同值的连续范围。
因此,让我们计算压缩 DP表,其中我们将行表示为(长度,值)对的列表。我们从f(1)= [(P,1)]开始,我们可以通过按递增顺序处理段并计算存储在f(i)中的长度的前缀和来从f(i)计算f(i + 1)
我实现此方法的总运行时间是 O(m(log P + n))。这是我使用的代码:
using ll=long long;
const int mod = 1000000007;
void add(int& x, ll y) { x = (x+y)%mod; }
int main() {
int n, P;
cin >> n >> P;
int x = 1;
vector<pair<int,int>> segments;
while(x <= P) {
int y = x+1, hi = P+1;
while(y<hi) {
int mid = (y+hi)/2;
if (P/mid < P/x) hi=mid;
else y=mid+1;
}
segments.push_back(make_pair(P/x, y-x));
x = y;
}
reverse(begin(segments), end(segments));
vector<pair<int,int>> dp;
dp.push_back(make_pair(P,1));
for (int i = 1; i <= n; ++i) {
int j = 0;
int sum_smaller = 0, cnt_smaller = 0;
vector<pair<int,int>> dp2;
for (auto it : segments) {
int value = it.first, cnt = it.second;
while (cnt_smaller + dp[j].first <= value) {
cnt_smaller += dp[j].first;
add(sum_smaller,(ll)dp[j].first*dp[j].second);
j++;
}
int pref_sum = sum_smaller;
if (value > cnt_smaller)
add(pref_sum, (ll)(value - cnt_smaller)*dp[j].second);
dp2.push_back(make_pair(cnt, pref_sum));
}
dp = dp2;
reverse(begin(dp),end(dp));
}
cout << dp[0].second << endl;
}
我需要通过处理数组来进行一些微优化以获得AC,但这些并不是真正相关的,所以我把它们留下了。
答案 1 :(得分:0)
如果顶点数小于邻接矩阵(A
)可以提供帮助。由于A^N
中的元素总和是不同路径的数量,因此路径是定向的。如果不是路径数i
元素之和/ 2.这是因为元素(i,j)
表示从顶点i
到顶点j
的路径数。
在这种情况下,DP可以使用相同的方法,使用推理,来自顶点n
的长度v
的路径数是所有路径长度n-1
的总和它的邻居。顶点i
的Neigbours是从1
到floor(Q/i)
的顶点。有了它,我们可以构造函数N(vertex, length)
,它表示给定长度给定顶点的路径数:
N(i, 1) = floor(Q/i),
N(i, n) = sum( N(j, n-1) for j in {1, ..., floor(Q/i)}.
所有定向路径的长度为sum( N(i,N) )
。