[MdOI R2] Odyssey 拓扑排序上DP
题意
若正整数(a,b)满足
- 存在正整数(c),使得(a imes 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})的权值组成完美数对
则称为完美路径
找出图中最长的完美路径,输出长度之和
[nleq10^5\
m leq 2 imes10^5\
k leq10
\wleq10^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();
}