这题使用拓扑排序的请注意,您的代码可能被hack

P3953 [NOIP2017 提高组] 逛公园

为了大家更方便思考问题 我给出以上数据的图 ![](https://leaf-salix.github.io/pictures/2019429hack1.png) ![](https://leaf-salix.github.io/pictures/2019429hack2.png) ![](https://leaf-salix.github.io/pictures/2019429hack3.png) ![](https://leaf-salix.github.io/pictures/2019429hack4.png) 这些的正确输出应该都是1,而有些题解输出-1 我对这个错误的分析是(我一开始也错了) 1.没过第1、2个数据是没有判断0环在不在可行路径中就直接输出-1 2.直接按所有0边的图拓扑排序会造成非0环上的点被当作0环上的点 什么意思呢? 请看第三幅图 如果仅仅是按0边建,您会发现入度为0的点是没有的,也就是5,6,7,2,3,4全在0环上?显然不是 注意!拓扑排序后剩下的不仅仅是0环,还有0环连出去的路径,所以您需要怎么做? 思考一下。。。 除了对所有0边建图跑拓扑排序,您可以对所有0边反向建,再跑拓扑排序 那么这时正图入度不为0且反图入度不为0的点才真的在0环上
by salix_leaf @ 2019-04-29 09:37:56


那么我的代码是 ```cpp #include<bits/stdc++.h> using namespace std; const int N=100005; const int M=200005; int n,m,k,p; struct Edge { int sum,h[N]; int v[M],w[M],nxt[M]; void adde(int x,int y,int z) { sum++; v[sum]=y; w[sum]=z; nxt[sum]=h[x]; h[x]=sum; } void clear() { sum=0; memset(h,0,sizeof(h));memset(v,0,sizeof(v)); memset(w,0,sizeof(w));memset(nxt,0,sizeof(nxt)); } }e,e0,une,une0; struct node { int pos,dis;int r; node(int x=0,int y=0):pos(x),dis(y){} friend bool operator <(node a,node b){return a.dis>b.dis;} } g[N]; bool cmp(node a,node b){return a.dis!=b.dis?(a.dis<b.dis):(a.r<b.r);} bool v[N]; priority_queue<node> q; void dijkstra1() { for(int i=1;i<=n;i++) g[i].pos=i,g[i].dis=0x7fffffff; memset(v,0,sizeof(v)); g[1].dis=0; q.push(node(1,0)); while(!q.empty()) { int x=q.top().pos;q.pop(); if(v[x]) continue; v[x]=1; for(int i=e.h[x];i;i=e.nxt[i]) { int y=e.v[i]; if(g[y].dis>1ll*g[x].dis+e.w[i]) { g[y].dis=g[x].dis+e.w[i]; q.push(node(y,g[y].dis)); } } } } int undis[N]; void dijkstra2() { memset(undis,127,sizeof(undis)); memset(v,0,sizeof(v)); undis[n]=0; q.push(node(n,0)); while(!q.empty()) { int x=q.top().pos;q.pop(); if(v[x]) continue; v[x]=1; for(int i=une.h[x];i;i=une.nxt[i]) { int y=une.v[i]; if(undis[y]>1ll*undis[x]+une.w[i]) { undis[y]=undis[x]+une.w[i]; q.push(node(y,undis[y])); } } } } bool ine0[N]; int ind[N]; int unind[N]; void topo1() { int cnt=0; queue<int> q; for(int i=1;i<=n;i++) if(ine0[i]&&ind[i]==0) g[i].r=++cnt,q.push(i); while(!q.empty()) { int x=q.front();q.pop(); for(int i=e0.h[x];i;i=e0.nxt[i]) { int y=e0.v[i]; ind[y]--; if(ind[y]==0) g[y].r=++cnt,q.push(y); } } } void topo2() { int cnt=N; queue<int> q; for(int i=1;i<=n;i++) if(ine0[i]&&unind[i]==0) g[i].r=--cnt,q.push(i); while(!q.empty()) { int x=q.front();q.pop(); for(int i=une0.h[x];i;i=une0.nxt[i]) { int y=une0.v[i]; unind[y]--; if(unind[y]==0) g[y].r=--cnt,q.push(y); } } } int f[55][N]; int pos[N]; int main() { int T; scanf("%d",&T); while(T--) { e.clear();e0.clear();une.clear();une0.clear(); memset(ine0,0,sizeof(ine0)); memset(ind,0,sizeof(ind)); memset(unind,0,sizeof(unind)); scanf("%d%d%d%d",&n,&m,&k,&p); int a,b,c; for(int i=1;i<=m;i++) { scanf("%d%d%d",&a,&b,&c); e.adde(a,b,c); une.adde(b,a,c); if(c==0) { ine0[a]=ine0[b]=1; ind[b]++;unind[a]++; e0.adde(a,b,c);une0.adde(b,a,c); } } dijkstra1(); dijkstra2(); topo1(); topo2(); bool ok=0; for(int i=1;i<=n;i++) if(ind[i]&&unind[i]&&1ll*g[i].dis+undis[i]<=1ll*g[n].dis+k)//g[i].dis+undis[i]+k<=g[n].dis {ok=1;break;} if(ok){printf("-1\n");continue;} sort(g+1,g+n+1,cmp); for(int i=1;i<=n;i++) pos[g[i].pos]=i; memset(f,0,sizeof(f)); f[0][pos[1]]=1; for(int more=0;more<=k;more++) { for(int x=1;x<=n;x++) { if(f[more][x]==0) continue; for(int i=e.h[g[x].pos];i;i=e.nxt[i]) { int y=e.v[i]; int tmp=g[x].dis+more+e.w[i]-g[pos[y]].dis; if(tmp<=k) { f[tmp][pos[y]]+=f[more][x]; while(f[tmp][pos[y]]>=p) f[tmp][pos[y]]-=p; } } } } int ans=0; for(int i=0;i<=k;i++) ans=(ans+f[i][pos[n]])%p; printf("%d\n",ans); } return 0; } ``` ***如果您发现您的数据可以hack我,请尽快把我叉掉,不要让我误导他人qwq*** ~~管理员您看我这么有诚意就加一个hack数据呗~~
by salix_leaf @ 2019-04-29 09:38:50


@[chen_zhe](/space/show?uid=8457)
by Smile_Cindy @ 2019-04-29 09:43:14


@[Alpha](/space/show?uid=87058) 谢谢qwq
by salix_leaf @ 2019-04-29 09:43:56


受教了
by Imakf @ 2019-04-29 09:47:08


经测试 题解中使用了拓扑排序的目前只有 [这个](https://www.luogu.org/blog/user34444/solution-p3953)qwq和[这个](https://www.luogu.org/blog/user26242/noip2017d1t3-xie-ti-bao-gao)可以过这组数据 tql!!!
by salix_leaf @ 2019-04-29 10:03:20


不用拓扑排序也可能被卡 如果判-1的时候,某个零环不能被任何一条合法路径覆盖,则必须忽略这个零环 也就是,如果两个方向的最短距离之和过大,则这个点需要删除
by Hope2075 @ 2019-04-29 10:12:46


@[i_m_a_](/space/show?uid=86649) 嗯我想我写到了这个呢qwq 上面:1.没过第1、2个数据是没有判断0环在不在可行路径中就直接输出-1
by salix_leaf @ 2019-04-29 10:19:36


@[salix_leaf](/space/show?uid=59700) 应该是吧 这是我去年写的 ```cpp bool test(){ for(int i=1;i<=n;i++){ //if((dis[i]+disa[i]<=disa[1]+k)){ if(iszero[i]){ return true; } //}else{ // head[i]=0; //} } return false; } ``` 我的做法:如果在可行路径上某个点在零环上,就判无解 注释掉判是否在可行路径上的部分就全WA了
by Hope2075 @ 2019-04-29 10:26:27


@[salix_leaf](/space/show?uid=59700) “除了对所有0边建图跑拓扑排序,您可以对所有0边反向建,再跑拓扑排序” 也可以卡: ``` 1 9 10 0 1000 1 5 1 5 9 1 5 2 0 2 3 0 3 4 0 4 2 0 5 6 0 6 7 0 7 8 0 8 6 0 ```
by Hope2075 @ 2019-04-29 10:37:24


| 下一页