[MdOI R2] Odyssey 拓扑排序上DP
[MdOI R2] Odyssey 拓扑排序上DP
题意
若正整数\(a,b\)满足
- 存在正整数\(c\),使得\(a \times b = c^k\)
则称为数对\((a,b)\)完美数对
有一个包含\(n\)个结点,\(m\)条边的有向无环图,这张图的每条边都有权值和长度两个属性。
如果一条路径\(P\)满足:
- \(P\)从起点开始经过依次为\(e_1,e_2,e_3...e_p\)这\(p\)条边,对于任意\(1 \leq i \leq p - 1\),\(e_i\)的权值和\(e_{i+1}\)的权值组成完美数对
则称为完美路径
找出图中最长的完美路径,输出长度之和
\[n\leq10^5\\
m \leq 2 \times10^5\\
k \leq10
\\w\leq10^5
\]
分析
首先处于一个有向无环图,自然希望通过\(dp\)来解决
先来处理给出的性质:
容易发现这里\(c\)是任意的,\(k\)是给定的,从唯一分解定理的角度考虑,我们只需要让每个质因子最终都是\(k\)的倍数即可。
所以处理方法就是对指数取模\(k\),要求剩下的指数能够满足,可以发现也是唯一对应的。
然后就考虑\(dp\)了,我们发现要到达当前边,必须通过前驱点的边的状态
\(dp[i][val]\)表示达到点\(i\),入边的边权为\(val\),所能得到的最大长度
\[dp[v][val] = max\{dp[u][inv(val)] + l\}
\]
然后在拓扑序上\(dp\)即可
代码
int n, m, k;
int indeg[maxn];
int ans;
unordered_map<int, int> mp[maxn];
int inv(int w) {
int res = 1;
for (int i = 2; i * i <= w; i++) {
int cnt = 0;
while (w % i == 0) w /= i, cnt++;
cnt %= k;
if (cnt) {
cnt = k - cnt;
while (cnt--) {
res *= i;
if (res > 100000) return -1;
}
}
}
if (w > 1) {
int cnt = k - 1;
while (cnt--) {
res *= w;
if (res > 100000) return -1;
}
}
return res;
}
int yu(int w) {
int res = 1;
for (int i = 2; i * i <= w; i++) {
int cnt = 0;
while (w % i == 0) w /= i, cnt++;
cnt %= k;
while (cnt--)
res *= i;
}
if (w > 1 && k != 1) return res * w;
else return res;
}
struct Edge {
int v;
int win, vin, val;
Edge(int _v, int _val, int _w) {
v = _v;
val = _val;
win = yu(_w);
vin = inv(_w);
}
};
vector<Edge> e[maxn];
void topo() {
queue<int> q;
for (int i = 1; i <= n; i++) if (!indeg[i]) q.push(i);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < e[u].size(); i++) {
Edge y = e[u][i];
mp[y.v][y.vin] = max(mp[y.v][y.vin], mp[u][y.win] + y.val);
ans = max(ans, mp[y.v][y.vin]);
indeg[y.v]--;
if (!indeg[y.v]) q.push(y.v);
}
}
printf("%d", ans);
}
int main() {
n = readint();
m = readint();
k = readint();
for (int i = 1; i <= m; i++) {
int a = readint();
int b = readint();
int w = readint();
int v = readint();
e[a].push_back(Edge(b, v,w));
indeg[b]++;
}
topo();
}