美团2018年CodeM大赛-复赛-B-软件包管理器(二分答案+深搜->有向图判环)

链接: https://www.nowcoder.com/acm/contest/152/B
来源:牛客网

题目描述

    点点现在有n个软件包。他想设计一个软件包管理器。不可避免地,他要解决软件包之间的依赖问题。
    一开始这些软件包之间没有依赖关系。但是每次点点会添加一条依赖关系a,b,表示软件包a依赖b。当这些软件包的依赖关系没有环的时候,那么这个软件包的管理器是好的,否则就是不好的。
    环的定义如下:
    对于任意k(k≥2)个软件包{a 1,a 2,...,a k},若对于所有的i<k满足a i依赖a i+1,且a 1=a k成立,则这k个软件包构成一个环。
    点点想让你回答他每次加入一条依赖关系之后这个软件包是不是好的,如果是好的那么输出1,否则输出0。同时,点点希望他在加入每一条关系之后你都能回答他,所以他会在读入中对数据进行加密。你只有回答了问题才能知道下一条依赖关系是什么。

输入描述:

第一行两个正整数n,m (1≤n≤100,000, 1≤m≤200,000),表示软件包的个数和操作个数。软件包的标号为1到n。
接下来m行,每行两个正整数u’, v’,表示加密后的依赖关系,保证u’≠v’。如果上一个回答为ans,实际的依赖关系为u, v,那么u’=(u+ans) mod n+1, v’=(v+ans) mod n+1。一开始的ans为0。

输出描述:

输出m行,每行一个数0或1,表示加入了一条依赖关系之后的回答。


题解:题目中的加密和解密无非就是给你一个强制在线的假象,但简单的思考后你会发现完全可以离线来做,因为答案只有0或者1,并且在出现0之后就一直是0了,因此我们考虑第一个0出现的位置即可,然后判环直接爆搜。。。

#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
#define mod 1000000007
#define maxn 100005
vector<int>q[maxn];
int n,m,ans,a[2*maxn],b[2*maxn],vis[maxn],flag,c[2*maxn],d[2*maxn];
void dfs(int x)
{
    if(flag) return;vis[x]=1;
    for(int i=0;i<q[x].size();i++)
    {
        if(vis[q[x][i]]==2) continue;
        if(vis[q[x][i]]==1)
        {
            flag=1;
            continue;
        }
        dfs(q[x][i]);
    }
    vis[x]=2;
}
int check(int x)
{
    flag=0;
    for(int i=1;i<=n;i++)
        q[i].clear(),vis[i]=0;
    c[1]=a[1]-1;d[1]=b[1]-1;
    for(int i=2;i<=x;i++)
    {
        c[i]=a[i]-2;
        if(c[i]==0) c[i]=n;
        if(c[i]==-1) c[i]=n-1;
        d[i]=b[i]-2;
        if(d[i]==0) d[i]=n;
        if(d[i]==-1) d[i]=n-1;
    }
    for(int i=1;i<=x;i++)
        q[c[i]].push_back(d[i]);
    for(int i=1;i<=n;i++)
    {
        if(!vis[i]) dfs(i);
        if(flag) return 0;
    }
    return 1;
}
int main(void)
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&a[i],&b[i]);
    int l=1,r=m,mid;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(check(mid))
            ans=mid,l=mid+1;
        else
            r=mid-1;
    }
    for(int i=1;i<=ans;i++)
        printf("1\n");
    for(int i=ans+1;i<=m;i++)
        printf("0\n");
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值