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\n");
        else printf("%d\n",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?我是真的服。

时间: 2024-08-29 22:51:58

NOIP2017D1T3逛公园——哎呦!的相关文章

TYVJ1427 小白逛公园

P1427 小白逛公园 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了.    一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的一些公园玩.小白当然希望选出的公园的分数总和尽量高咯

线段树--小白逛公园nkoj1316

小白逛公园 Time Limit:20000MS  Memory Limit:65536K Case Time Limit:2000MS Description 小新经常陪小白去公园玩,也就是所谓的遛狗啦-在小新家附近有一条"公园路",路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了. 一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的

Luogu P3953【NOIP2017】逛公园【最短路+拓扑排序+动态规划】

题目描述 策策同学特别喜欢逛公园.公园可以看成一张NN个点MM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,NN号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. 策策每天都会去逛公园,他总是从1号点进去,从NN号点出来. 策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间.如果1号点 到NN号点的最短路长为dd,那么策策只会喜欢长度不超过d + Kd+K的路线. 策策

luogu 3953 逛公园

noip2017 D1T3 逛公园 某zz选手看到数据范围直接就最短路计数了,结果写错了爆零 题目大意: N个点M条边构成的有向图,且没有自环和重边.其中1号点是起点,N号点是公园的终点,每条边有一个非负权值, 代表经过这条边所要花的时间 如果1号点到N号点的最短路长为d,那么策策只选择长度不超过d + K的路线 求总共有多少条满足条件的路线 为避免输出过大,答案对P取模. 如果有无穷多条合法的路线,请输出?1 思路: 首先需要求出最短路用spfa 然后我们dfs的时候dp 具体见注释 1 #i

【比赛】NOIP2017 逛公园

考试的时候灵光一闪,瞬间推出DP方程,但是不知道怎么判-1,然后?然后就炸了. 后来发现,我只要把拓扑和DP分开,中间加一个判断,就AC了,可惜. 看这道题,我们首先来想有哪些情况是-1:只要有零环在满足题目要求的路径上,那么这条路径就可以不停地走,于是就-1了. 如何判有没有零环呢? 机械化地两遍不同方向的SPFA,就知道某个点在不在最短路上,以此建一个最短路图,在最短路图上找零环.于是就拓扑啦.稍加判断就解决了整个题目最关键的-1. 接下来就是DP了,设f[i][j]表示走到i点,走过路程已

[NOIp 2017]逛公园

Description 策策同学特别喜欢逛公园.公园可以看成一张$N$个点$M$条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,$N$号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. 策策每天都会去逛公园,他总是从1号点进去,从$N$号点出来. 策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间.如果1号点 到$N$号点的最短路长为$d$,那么策策只会喜欢长度不超过$d

[NOIP2017] 逛公园

[NOIP2017] 逛公园 题目大意: 给定一张图,询问长度 不超过1到n的最短路长度加k 的1到n的路径 有多少条. 数据范围: 点数\(n \le 10^5\) ,边数\(m \le 2*10^5\) 题目解法 两个月后再看也不是太难,自己就能独立思考出来. 首先是判-1的问题,显然能产生-1的只有0环. 所以把0环都找出来, 然后检查一下\(dis[\)\(1\),环\(]\) + \(dis[\)环,\(n]\) 是否小于等于 \(dis[1,n]+K\)即可. 如果不是无限路径的话,

P3953 NOIP2017 d1t3 逛公园

题目描述 策策同学特别喜欢逛公园.公园可以看成一张NN 个点MM 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,NN 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. 策策每天都会去逛公园,他总是从1号点进去,从NN 号点出来. 策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间.如果1号点 到NN 号点的最短路长为dd ,那么策策只会喜欢长度不超过d + Kd+K 

洛谷3953:逛公园——题解

https://www.luogu.org/problemnew/show/P3953 策策同学特别喜欢逛公园.公园可以看成一张n个点m条边构成的有向图,且没有自环和重边.其中1号点是公园的入口,n号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间. 策策每天都会去逛公园,他总是从1号点进去,从n号点出来. 策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间.如果1号点到n号点的最短路长为d