zoukankan      html  css  js  c++  java
  • NOIP2017D1T3逛公园——哎呦!

    #include<cstdio>
    #include<cstring>
    #define MXN 100001
    #define MXM 200001
    #define MXK 51
    int afst[MXM],anxt[MXM],av[MXM],aw[MXM];
    int bfst[MXM],bnxt[MXM],bv[MXM],bw[MXM];
    int dis[MXN],queue[10*MXN];
    int f[MXN][MXK],vis[MXN][MXK];
    int n,m,k,p,t,etop,ans;
    bool onzer,zero[MXN];
    int read(){
        int x=0,w=1;
        char c=getchar();
        while(c<'0'||c>'9'){
            if(c=='-') w=-1;
            c=getchar();
        }
        while(c>='0'&&c<='9'){
            x=(x<<3)+(x<<1)+(c-'0');
            c=getchar();
        }
        return x*w;
    }
    void add(int x,int y,int z){
        anxt[++etop]=afst[x];
        afst[x]=etop;
        av[etop]=y;
        aw[etop]=z;
        bnxt[etop]=bfst[y];
        bfst[y]=etop;
        bv[etop]=x;
        bw[etop]=z;
        return;
    }
    void spfa(){
        int head=0,tail=0;
        queue[tail++]=1;
        while (head!=tail){
            int now=queue[head];
            for (int i=afst[now];i!=-1;i=anxt[i]){
                int j=av[i];
                if (dis[now]+aw[i]<dis[j]){
                    dis[j]=dis[now]+aw[i];
                    if (!zero[j]){
                        zero[j]=1;
                        queue[tail]=j;
                        if(tail+1!=10*MXN) tail++;
                        else tail=0;
                    }
                }
            }
            if(head+1!=10*MXN) head++;
            else head=0;
            zero[now]=0;
        }
        return;
    }
    int ddf(int i,int h){
        if(~f[i][h])return f[i][h];
        vis[i][h]=1;
        f[i][h]=0;
        for(int y=bfst[i];y!=-1;y=bnxt[y]){
            int j=bv[y],t=dis[i]-dis[j]+h-bw[y];
            if(t<0)continue;
            if(vis[j][t]) onzer=0;
            f[i][h]+=ddf(j,t);
            f[i][h]%=p;
        }
        vis[i][h]=0;
        return f[i][h];
    }
    void init(){
        n=read();
        m=read();
        k=read();
        p=read();
        etop=0;
        memset(afst,-1,sizeof(afst));
        memset(anxt,-1,sizeof(anxt));
        memset(av,0,sizeof(av));
        memset(aw,0,sizeof(aw));
        memset(bfst,-1,sizeof(afst));
        memset(bnxt,-1,sizeof(anxt));
        memset(bv,0,sizeof(av));
        memset(bw,0,sizeof(aw));
        memset(vis,0,sizeof(vis));
        memset(zero,0,sizeof(zero));
        memset(f,-1,sizeof(f));
        for(int i=1;i<=n;i++) dis[i]=0x6fffffff;
        for(int i=1;i<=m;i++){
            int x=read(),y=read(),z=read();
            add(x,y,z);
        }
        zero[1]=1;
        dis[1]=0;
        ans=0;
        onzer=1;
        f[1][0]=1;
        return;
    }
    int main(){
        t=read();
        while (t--){
            init();
            spfa();
            for(int i=0;i<=k;i++){
                ans+=ddf(n,i);
                ans%=p;
            }
            ddf(n,k+1);
            if(!onzer) printf("-1
    ");
            else printf("%d
    ",ans);
        }
        return 0;
    }
    逛公园

    这真是一道神奇的题目。

    首先必须说明,这是我看了题解后写的。凭本人的水平是绝对A不了这道题的(毕竟考试时两天两道DP一道都没有看出来(或者说至少没有试着去写))。

    不过这道题思路还是比较明确的:

    首先为了得知d的值无论如何都要跑一边最短路来求(本人用的SPFA,但是自己觉得dj更好)。之后纯粹暴力的想法就是dfs,到达n之后判断这条路径上的权值和是否满足条件,dfs中判断路径上的0环,顺便用<=d+K特判(并且有些0环对答案并没有影响)。稍微思索一下就明白这样的时间复杂度为O(跑不过)

    上面提到的不重要0环可以用正反两次最短路,记下两个dis数组来判断这一点的0环是否对答案有贡献(意为:在点i处发现一个0环,利用dis1[i]+dis2[i]<=d+K判断,成立则0环对最终方案有影响(明显若不等号不成立则方案中不存在经过i点的路径,则此时暂时不用考虑这个0环))。

    通过观察到K<=50,认为可以DP,其中某一个状态与K有关。状态f[i][j]表示路径上点i,此路径超过最短路长度为j

    状态转移方程可得:f[i][j]=Σf[u][t]  ((u,i) in G,t=dis[i]-dis[u]+j-w[i,u])

    其中,t=dis[i]-dis[u]+j-w[i,u]的方程计算经过(u,i)路径时,若i点时权值和超过j,则u点时权值和超过t。整理前等式像这样子:t+(dis[u]+w[i,u]-dis[i])=j

    因此明显可以进行容易的DP,复杂度为O(MK)。以上方程适用于反向DP,初始化为f[1][0]=1,答案ans=∑f[n][i](0<=i<=K)

    接下来继续处理判断0环:

    第一种方法是像前面某一段提到的,由于是图上DP,状态随dfs转移,因此利用一个vis数组,当dfs遇到vis为真时,利用dis1[i]+dis2[i]<=n+K判断0环是否影响答案,影响则输出-1否则继续得佛斯;

    第二种方法是我抄了题解的:dfs时再用vis[i][j]记录之前是否出现过同样的状态,出现过则输出-1

    实际上最后还额外进行了一次dfs(n,K+1),由于题解写了,并且删了样例都过不了,就交了。据同机房大神说,是因为f[1][0]置为1,所以需要这样搞(同机房另一大神亲身经历告诉我们,删了这句话,样例不过,照样A题)。

    由于状态保存的是严格到达i点路径超过最短路j,因此保证上面那一段对0环判断的有效性,并且(貌似?)没有什么后效性(据机房另一位大神透露,后效性来自转一圈后回到原点,又没有超限,下次从n开始dfs,还会dfs到这个状态,导致方案重复。代码里这样搞貌似不会这样,大约是因为第二个状态是顺着推,又在图上倒着推导致的?)。不过我不会证,就不能说什么了。

    说一点题外话:

    看题解时发现一些小技巧,比如数组模拟邻接表可以只开四个数组,还有代码里SPFA手打队列时对head++和tail++的处理,保证了不会RE,还有貌似可以(f+=d)%=p这样写?不过我没有这样。并且还发现2147483647==0x7fffffff,但是INF设成这个加减运算时会出问题,我设成0x6fffffff。

    顺便一说刚学到head++tail++防RE的技巧时还用错了,反而导致十个点全RE了......我是真的菜,发现这一点时大喊哎呦。

    就这样吧。

    PS:
    最近真的是遇到许多玄学RE,并且还一直调不对。今天下午调这题时dfs的参数还会莫名改变,明明所有边权值都是0,第二个状态还能搜到1?我是真的服。

  • 相关阅读:
    ie7不支持inline-block
    使padding的值不影响整体块的大小
    网页在不同屏幕下的自适应
    CSS:line-height:行高
    用css制作三角形,理解
    js之setTimeOut();
    jQuery:has()和jQuery:contains()区别
    前端开发之jQuary--可见筛选选择器
    python基础知识--10Lambda匿名函数、三元表达式及map、reduce、filter
    python基础知识--9迭代器生成器闭包与装饰器
  • 原文地址:https://www.cnblogs.com/halifuda/p/8007176.html
Copyright © 2011-2022 走看看