https://www.luogu.com.cn/problem/P6381
一个不怎么要脑子的做法...代码也不难写...
考虑在DAG上DP
令(f_{i,j})表示以点(i)为结尾,且上一条边权值为(j)的最长路径数量。
那么有转移
(f_{v,b} = max { f_{u,a} + l })((ab=c^k)且存在边((u,v,w)))
先不管这样做的空间...
考虑如何做到转移时满足(ab = c^k)这个条件
将(a)质因数分解:(a = p_1^{e_1}cdots p_s^{e_s})
然后将所以指数对(k)取模,即令(e'_i = e_i mod k)
然后得到一个数(a' = p_1^{e'_1} cdots p_s ^{e'_s})
设(b' = p_1^{k-e'_1} cdots p_s ^{k-e'_s})
即对于(b)质因数分解后也做这样的操作可以得到(b')。
同时对于(a),与之匹配的(b)做这个操作后得到的(b')是唯一的,这也非常方便算出来。
那么状态变一变,改成(f_{i,j'}),即以(i)结尾,上一条边做上述变换后得到的数为(j')时的最长路径。
但数太大了,数组开不下,而且直接把数带进去也不好算
注意到一个数的质因子个数不会太多,我们可以把他的质因数分解作为第二维(指数对(k)取模后的)然后DP。
不想hash的话开个map记一下状态好了,DP就直接按拓扑序去转移
具体还是看代码吧...
跑的超慢的(Code):
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
typedef pair<int,int> pii;
typedef vector<pii> pfac;
pfac factor(int n,int k)
{
pfac ans;
for(int i=2;i*i<=n;i++)
{
int cnt=0;
while(n%i==0) (++cnt)>=k?cnt-=k:0,n/=i;
if(cnt) ans.pb(mp(i,cnt));
}
if(n>1&&k!=1) ans.pb(mp(n,1));
return ans;
}
pfac mpfac(const pfac &pf,int k)
{
pfac ans;
for (auto p:pf) ans.pb(mp(p.fi,k-p.se));
return ans;
}
const int N=1111111;
int n,m,k;
struct edge
{
int u,v,w,l,nxt;
pfac pf;
}e[N];
int fst[N],ec=0,ind[N],ans=0;
void ade(int u,int v,int w,int l)
{
e[++ec]=(edge){u,v,w,l};
e[ec].pf=factor(w,k);
e[ec].nxt=fst[u],fst[u]=ec;
++ind[v];
ans=max(ans,l);
}
int tps[N],tn;
void topo()
{
tn=0;
queue<int> q;
for(int i=1;i<=n;i++) if(ind[i]==0) q.push(i);
while(q.size())
{
int u=q.front(); q.pop();
tps[++tn]=u;
for(int i=fst[u];i;i=e[i].nxt)
{
int v=e[i].v; --ind[v];
if (ind[v]==0) q.push(v);
}
}
}
map<pfac,int> f[N];
void dp()
{
for(int i=1;i<=tn;i++)
{
int u=tps[i];
for(int j=fst[u];j;j=e[j].nxt)
{
int v=e[j].v;
pfac pf=e[j].pf,mpf=mpfac(pf,k);
f[v][mpf]=max(f[v][mpf],f[u][pf]+e[j].l);
ans=max(ans,f[v][mpf]);
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1,u,v,w,l;i<=m;i++)
scanf("%d%d%d%d",&u,&v,&w,&l),ade(u,v,w,l);
topo(),dp();
printf("%d
",ans);
return 0;
}