zoukankan      html  css  js  c++  java
  • cf102012J. Rikka with An Unnamed Temple

    题目描述

    题解

    考虑 $ 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;}
  • 相关阅读:
    Cocos Creator代码编辑环境配置
    CocosCreator编辑器界面
    Colored Sticks (并查集+Trie + 欧拉路)
    子序列 NYOJ (尺取法+队列+hash) (尺取法+离散化)
    相同的雪花 Hash
    F
    逆序数
    士兵杀敌5 前缀数组
    Color the ball 线段树 区间更新但点查询
    士兵杀敌(二) 线段树
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/15490117.html
Copyright © 2011-2022 走看看