题解
考虑 $ ext{dp}$ : $f_{i,j}$ 表示从 $1$ 到第 $i$ 个点,余数为 $j$ 的最大值和方案数。
因为是 $ ext{DAG}$ ,所以 $ ext{dp}$ 是按照拓扑序转移的。
所以可以考虑做一个前缀的 $ ext{dp}$ 和后缀的 $ ext{dp}$ ,如果不经过点 $i$ ,那就意味着有一次转移跨过了 $i$ 。
因此枚举每条边,它产生的答案就可以贡献给这两个点之间的点。
用线段树维护即可。效率: $O(nlogn)$ 。
需要特判一下拓扑序小于 $1$ 和大于 $n$ 的点。
代码
#include<bits/stdc++.h> #define LL long long using namespace std; const int N=1e5+5,M=2e5+5,P=1e9+7; int T,n,m,w[N],c[N],in[N],V[2][M],nx[2][M],t,hd[2][N],q[N],a,b,p[N]; struct O{ LL v;int w; void hb(LL _v,int _w){ if (_v>v) v=_v,w=0; if (_v==v) (w+=_w)%=P; } }f[2][N][100],g[N<<2],h[N]; void add(int o,int u,int v){ nx[o][t]=hd[o][u];V[o][hd[o][u]=t]=v; } #define Ls k<<1 #define Rs k<<1|1 #define mid ((l+r)>>1) void build(int k,int l,int r){ g[k]=(O){-1,0}; if (l==r) return; build(Ls,l,mid);build(Rs,mid+1,r); } void upd(int k,int l,int r,int L,int R,O F){ if (L<=l && r<=R) return g[k].hb(F.v,F.w); if (mid>=L) upd(Ls,l,mid,L,R,F); if (mid<R) upd(Rs,mid+1,r,L,R,F); } void go(int k,int l,int r,O F){ if (~g[k].v) F.hb(g[k].v,g[k].w); if (l==r){h[q[l]]=F;return;} go(Ls,l,mid,F);go(Rs,mid+1,r,F); } void work(){ scanf("%d%d",&n,&m);t=0; for (int i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]), hd[0][i]=hd[1][i]=in[i]=0; for (int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),t++, add(0,u,v),add(1,v,u),in[v]++; scanf("%d%d",&a,&b); for (int j=0;j<a;j++) for (int i=1;i<=n;i++) f[0][i][j]=f[1][i][j]=(O){-1,0}; f[0][1][w[1]%a]=(O){c[1],1}; f[1][n][w[n]%a]=(O){c[n],1};t=0; for (int i=1;i<=n;i++) if (!in[i]) q[++t]=i; for (int u,i=1;i<=t;i++){ p[u=q[i]]=i; for (int i=hd[0][u];i;i=nx[0][i]) if (!(--in[V[0][i]])) q[++t]=V[0][i]; } for (int i=1,u;i<=n;i++) for (int j=hd[1][u=q[i]],v;j;j=nx[1][j]) for (int k=0,l;k<a;k++) if (~f[0][v=V[1][j]][k].v) l=(k+w[u])%a,f[0][u][l].hb(f[0][v][k].v+c[u],f[0][v][k].w); for (int i=n,u;i;i--) for (int j=hd[0][u=q[i]],v;j;j=nx[0][j]) for (int k=0,l;k<a;k++) if (~f[1][v=V[0][j]][k].v) l=(k+w[u])%a,f[1][u][l].hb(f[1][v][k].v+c[u],f[1][v][k].w); build(1,1,n); for (int u=1;u<=n;u++) for (int i=hd[0][u],v;i;i=nx[0][i]){ v=V[0][i]; if (p[u]+1<p[v]){ O F=(O){-1,0}; for (int k=0,l;k<a;k++){ l=(b-k+a)%a; if ((~f[0][u][k].v) && (~f[1][v][l].v)) F.hb(f[0][u][k].v+f[1][v][l].v,1ll*f[0][u][k].w*f[1][v][l].w%P); } if (~F.v) upd(1,1,n,p[u]+1,p[v]-1,F); } } if (p[1]>1) upd(1,1,n,1,p[1]-1,f[0][n][b]); if (p[n]<n) upd(1,1,n,p[n]+1,n,f[0][n][b]); go(1,1,n,(O){-1,0}); for (int i=1;i<=n;i++) if (~h[i].v) printf("%lld %d ",h[i].v,h[i].w); else puts("-1"); } int main(){for (scanf("%d",&T);T--;work());return 0;}