题目描述
题解
考虑 dp \text{dp} dp : f i , j f_{i,j} fi,j 表示从 1 1 1 到第 i i i 个点,余数为 j j j 的最大值和方案数。
因为是 DAG \text{DAG} DAG ,所以 dp \text{dp} dp 是按照拓扑序转移的。
所以可以考虑做一个前缀的 dp \text{dp} dp 和后缀的 dp \text{dp} dp ,如果不经过点 i i i ,那就意味着有一次转移跨过了 i i i 。
因此枚举每条边,它产生的答案就可以贡献给这两个点之间的点。
用线段树维护即可。效率: O ( n l o g n ) O(nlogn) O(nlogn) 。
需要特判一下拓扑序小于 1 1 1 和大于 n n 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\n",h[i].v,h[i].w);
else puts("-1");
}
int main(){for (scanf("%d",&T);T--;work());return 0;}