Leaf-Salix.github.io

my blog

View on GitHub

前言

虽然已经发布了讨论

但是想了想还是写一篇题解尝试让更多人发现这个问题

(其实在高赞题解的讨论中已经有人指出这个问题,但是好像没人发题解指出,我觉得我还是在退役前多发光发热QAQ)

正文

我是一只即将退役的蒟蒻QAQ

我在退役前发现了我可以hack我自己QAQ

我还发现这题的题解中可能大部分拓扑排序的写法都会被以下数据hack(为了装逼qwq重点提一下还能把第一篇目前139赞的题解中的第一种含拓扑排序的写法hack哦qwq)

4 
6 7 9 1234
1 2 1
2 4 9
4 5 0
5 4 0
5 3 1
3 6 1
2 3 0
5 7 10 10000000
1 5 2
1 2 10000
1 3 10000
3 4 0
4 2 0
2 3 0
3 5 10000
8 8 0 10
1 2 1
2 3 0
3 4 0
4 8 1
5 2 0
6 5 0
7 6 0
5 7 0
8 8 0 10
1 2 1
2 3 0
3 4 0
4 8 1
2 5 0
6 5 0
7 6 0
5 7 0

这4组hack数据中的第一组来源于讨论Ycrpro1

第二组来源于lleo

后两组是我这个小蒟蒻的QAQ

为了大家更方便思考问题

我给出以上数据的图

这些的正确输出应该都是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环上

那么我的代码是

#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

后言

我在进行了一些测试后发现目前(2019.4.29)的拓扑排序题解中只有这个这个可以过这组数据

我太菜了好像并不能看懂第一篇分层图的写法(我不会分层图qwq

但是第二篇我大概明白了意思,它能够过这个数据是因为它是先找出可行路径,再在可行路径上用拓扑排序判断有没有0环;而我是先用拓扑排序,在看拓扑排序后的0环在不在可行路径上(请您思考我这样讲有没有问题,我能力有限,暂时不能发现其中是否有问题)

那么总的来说还是要对于每种解法有自己的思考,不能尽信,这句话与您共勉(共勉个鬼嘞我退役了QAQ

本人太菜QAQ,如果有错误请指出哦QAQ


2019.4.29