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 \bmod 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\n",ans);
return 0;
}