bzoj 1064 图论+dfs

7 篇文章 0 订阅
7 篇文章 0 订阅

题意:n个人带着m类面具,其中第i类面具能看到第i % m +1类的面具,给定一些a能看见b的关系,问m的最大最小值,要求m满足m>=3

图论好题,作为一名蒟蒻我觉得考场上我就输出-1 -1 就可以了...(洛谷能得一个点)

推荐一个十分好的带图的题解:http://blog.csdn.net/qpswwww/article/details/44044229

这里简述一下分类讨论:

(一)存在环 (单独这一种情况,洛谷能过7个点)

求出每个环包括的点的个数,每个环一定能被正好拆成k个循环才能满足题意和已知,

所以此时的最大值为各个环大小的最大公约数,最小值当然就是最大公约数大于3的最小因子

当gcd小于3时无解

(二)没有环

此时最大值等于每个联通分量里最长链长度的累加(一开始naive的认为是取max后来才反应过来是去sum......)

由于没有环的限制(不用看回去),所以最小值当然就是3

当累加和小于3时无解

=======我是一本道和口胡的分界线_(:з」∠)_===========

下面开始口胡

原图中给出的关系建边权为1,同时建反边边权为-1,把有向边变成无向边

因为当两个点同时指向一个点时,这三个点中有一个点是确定的那么这三个就是确定的,所以完全可以把它们三个缩成一个点,解决了链之间的限制关系(具体参见上面推荐的博客)

在实现中我们同样先判断是否有环,有的话就按照第一种处理没有的话再按照第二种处理

对于判断是否有环和同时更新答案,利用dfs,每找到一个环就求一下gcd(注意是绝对值),如果最后gcd不是0那么说明有环

对于求出每个联通分量里最长链的长度,如果我们访问到了一个已经访问过的点p 那么 max{d[x]+len[q]-d[p]} (d[x]表示x到该链第一个dfs的点的距离(“缩点”以后的))

我并不知道我在说什么,但是 把有向边转化成无向边(强制成环)的思想 和 这道题性质(能看见的条件) ,二者结合的还是十分巧妙的(不得不跪)

var
        n,m,a,b,ans,l   :longint;
        i               :longint;
        maxn,minn       :longint;
        vis             :array[0..100010] of boolean;
        d,num,last      :array[0..100010] of longint;
        pre,other,len   :array[0..2000010] of longint;
function gcd(a,b:longint):longint;
begin
   if b=0 then exit(a) else exit(gcd(b,a mod b));
end;

procedure connect(x,y,z:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
   len[l]:=z;
end;

procedure dfs1(x:longint);
var
     p,q:longint;
begin
   vis[x]:=true;
   q:=last[x];
   while q<>0 do
   begin
      p:=other[q];
      if not vis[p] then
      begin
         num[p]:=num[x]+len[q];
         dfs1(p);
      end else ans:=gcd(ans,abs(num[x]+len[q]-num[p]));
      q:=pre[q];
   end;
end;

procedure dfs2(x:longint);
var
        p,q:longint;
begin
   vis[x]:=true;
   if d[x]>maxn then maxn:=d[x];
   if d[x]<minn then minn:=d[x];
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if not vis[p] then
      begin
         d[p]:=d[x]+len[q];
         dfs2(p);
      end;
      q:=pre[q];
   end;
end;

begin
   read(n,m);
   for i:=1 to m do
   begin
      read(a,b);
      connect(a,b,1);
      connect(b,a,-1);
   end;
   //
   for i:=1 to n do if not vis[i] then dfs1(i);
   if (ans<>0) then
   begin
      if ans<3 then writeln(-1,' ',-1) else
      begin
         for i:=3 to ans do
           if ans mod i=0 then break;
         writeln(ans,' ',i);
      end;
      exit;
   end;
   //
   fillchar(vis,sizeof(vis),false);
   for i:=1 to n do
     if not vis[i] then
     begin
        maxn:=0; minn:=0;
        dfs2(i);
        inc(ans,maxn-minn+1);
     end;
   if ans<3 then writeln(-1,' ',-1)
     else writeln(ans,' ',3);
end.
——by Eirlys


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值