cf102012J. Rikka with An Unnamed Temple

题目描述

题解

考虑 $\text{dp}$ : $f_{i,j}$ 表示从 $1$ 到第 $i$ 个点,余数为 $j$ 的最大值和方案数。

因为是 $\text{DAG}$ ,所以 $\text{dp}$ 是按照拓扑序转移的。

所以可以考虑做一个前缀的 $\text{dp}$ 和后缀的 $\text{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\n",h[i].v,h[i].w);
        else puts("-1");
}
int main(){for (scanf("%d",&T);T--;work());return 0;}

 

posted @ 2021-10-31 17:14  xjqxjq  阅读(62)  评论(0)    收藏  举报