BestCoder Round #74 (div.2) T4 Toposort HDOJ 5638

/*
这道题的思路是使用利用优先队列将字典序作为第一关键字,将普通拓扑排序中的判定入度等于零变成入度小于等于k即可,但是如果每一次排出来一个就从新扫一遍也未免太慢,必爆的节奏(^o^)/~所以我们使用一种诡异优化来解决这个问题:

*/

#include<iostream>
        #include<algorithm>
        #include<queue>
        #include<cstdio>
        #include<cstring>
        using namespace std;
        priority_queue <int> q;//优先队列;
        vector <int> a[100500];//存储点关系的vector;
        long long T,n,m,k,ui,vi ,mod=1000000007, ans[100500],ru[200500];//ru记录入度;
        bool vis[100500];//记录这个点是否已经被排出;
        bool IN[100500];//记录这个点是否在优先队列里;
        long long tot,S;//tot就是用来每次加一来向ans里放答案的;
        int main()
        {
                scanf( "%d",&T );
                while( T-- )
                {
                        S=0;
                        tot=0;
                        scanf("%d%d%d",&n,&m,&k);
                        memset(vis,0,sizeof(vis));
                        memset(ru,0,sizeof(ru));
                        memset(IN,0,sizeof(IN));
                        for(int i=1;i<=n;i++)
                                a[i].clear();
                                //这一堆清零是必须的;
                        for(int i=1;i<=m;i++)
                        {
                                scanf("%d%d",&ui,&vi);
                                a[ui].push_back(vi);//利用vector连边;
                                ru[vi]++;//入度加一;
                        }
                        for (int i=1;i<=n;i++)
                        {
                            q.push(-i);//优先队列是个最大堆,所以把负值存进去就能做到字典序最小啦\(^o^)/~;
                            IN[i]=1;//现在它在优先队列里;
                        }
                        while(!q.empty())//不为空即执行;
                        {
                                int now=-q.top();//当前要处理的点;
                                q.pop();//将它弹出(这个点现在的状态是等待处理);
                                if( !IN[now] ) continue;//判定无解情况;
                                IN[now]=0;//现在它不在优先队列里了;
                                if (ru[now]<=k)//可以删边弹出;
                                {
                                        vis[now]=1;//现在它被排出来了;
                                        k-=ru[now];//可删边数减少;
                                        ans[++tot]=now;//记录答案;
                                        int len=a[now].size();
                                        for (int j=0;j<len;j++)//扫描其每一个连向的点;
                                        {
                                                int son=a[now][j];//处理儿子;
                                                ru[son]--;//因为它被弹出,现在它的儿子的入度要减一;
                                                if (IN[son]==0 && vis[son]==0)如果它不在优先队列里并且它也没有被排出来(也就是说不满足条件,不是字典序最小也没法排出来);
                                                {
                                                        q.push(-son);把它放回去(这一段最好自己模拟一遍就会懂);
                                                        IN[son]=1;//现在它又回到优先队列里了;
                                                }
                                        }
                                }
                        }
                        for(int i=1;i<=n;i++)
                        {
                                S+=(ans[i]*i)%mod;
                                S%=mod;
                        }
                        printf("%I64d\n",S);
                }
                return 0;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值