bzoj1093: [ZJOI2007]最大半连通子图

传送门
首先用tarjan缩点,重建图。点的权值为DAG中点的个数。
跑一遍最长路水过。

uses math;
var
  a,b,c,d,tr,lo,vis,ss,s,eda,edb,f,g,ge,co,zhan,tt,li:array [0..1000005] of longint;
  n,sum,h,t,ansx,ansy,m,modd,i,p,lm,j,top:longint;
procedure ins(i:longint);
  begin c[i]:=d[a[i]]; d[a[i]]:=i; end;
procedure tarjan;
  begin
    inc(h); tr[li[li[0]]]:=h; lo[li[li[0]]]:=h; vis[li[li[0]]]:=1;
    inc(t); f[t]:=li[li[0]]; s[li[li[0]]]:=1; ss[li[li[0]]]:=1;
    tt[li[li[0]]]:=d[li[li[0]]];
    while (tt[li[li[0]]]<>0) do begin
      if (s[b[tt[li[li[0]]]]]=0) then 
        begin li[li[0]+1]:=b[tt[li[li[0]]]]; inc(li[0]);
          tarjan; lo[li[li[0]]]:=min(lo[li[li[0]]],lo[b[tt[li[li[0]]]]]); end
        else if (ss[b[tt[li[li[0]]]]]=1) then
          lo[li[li[0]]]:=min(lo[li[li[0]]],tr[b[tt[li[li[0]]]]]);
      tt[li[li[0]]]:=c[tt[li[li[0]]]];
    end;
    if tr[li[li[0]]]=lo[li[li[0]]] then begin
      inc(sum);
      while (f[t+1]<>li[li[0]]) do
        begin ss[f[t]]:=0; co[f[t]]:=sum; inc(ge[sum]); dec(t); end;
    end;
    dec(li[0]);
  end;
procedure kp(l,r:longint);
  var i,j,m1,m2:longint;
  begin
    i:=l; j:=r; m1:=eda[(l+r) div 2]; m2:=edb[(l+r) div 2];
    while (i<=j) do begin
      while (eda[i]<m1) or ((eda[i]=m1) and (edb[i]<m2)) do inc(i);
      while (eda[j]>m1) or ((eda[j]=m1) and (edb[j]>m2)) do dec(j);
      if (i<=j) then begin
        p:=eda[i]; eda[i]:=eda[j]; eda[j]:=p;
        p:=edb[i]; edb[i]:=edb[j]; edb[j]:=p;
        inc(i); dec(j);
    end; end;
    if (l<j) then kp(l,j);
    if (i<r) then kp(i,r);
  end;
procedure work;
  begin
    tt[li[li[0]]]:=d[li[li[0]]]; g[li[li[0]]]:=1; vis[li[li[0]]]:=1;
    while (tt[li[li[0]]]<>0) do begin
      if (vis[b[tt[li[li[0]]]]]=0) then begin li[li[0]+1]:=b[tt[li[li[0]]]]; inc(li[0]); work; end;
      if (f[b[tt[li[li[0]]]]]>f[li[li[0]]]) then begin f[li[li[0]]]:=f[b[tt[li[li[0]]]]]; g[li[li[0]]]:=0; end;
      if (f[b[tt[li[li[0]]]]]=f[li[li[0]]]) then g[li[li[0]]]:=(g[li[li[0]]]+g[b[tt[li[li[0]]]]]) mod modd;
      tt[li[li[0]]]:=c[tt[li[li[0]]]];
    end;
    f[li[li[0]]]:=f[li[li[0]]]+ge[li[li[0]]];
    dec(li[0]);
  end;
begin
  read(n,m,modd);
  for i:=1 to m do
    begin read(a[i],b[i]); ins(i); end;
  for i:=1 to n do
    if (vis[i]=0) then begin h:=0; t:=0; li[0]:=1; li[1]:=i; tarjan; end;
  m:=0;
  for i:=1 to n do
    begin
      p:=d[i];
      while (p<>0) do begin
        if (co[a[p]]<>co[b[p]]) then
          begin inc(m); eda[m]:=co[a[p]]; edb[m]:=co[b[p]]; end;
        p:=c[p];
      end;
    end;
  kp(1,m);
  lm:=1;
  for i:=2 to m do if (eda[i]<>eda[i-1]) or (edb[i]<>edb[i-1]) then
    begin inc(lm); eda[lm]:=eda[i]; edb[lm]:=edb[i]; end;
  a:=edb; b:=eda; n:=sum; m:=lm;
  fillchar(c,sizeof(c),0);
  fillchar(d,sizeof(d),0);
  fillchar(f,sizeof(f),0);
  fillchar(tt,sizeof(tt),0);
  for i:=1 to m do ins(i);
  fillchar(vis,sizeof(vis),0);
  for i:=1 to n do
    if vis[i]=0 then begin li[0]:=1; li[1]:=i; work; end;
  ansx:=0; ansy:=0;
  for i:=1 to n do
    begin
      if (ansx<f[i]) then begin ansy:=0; ansx:=f[i]; end;
      if (ansx=f[i]) then ansy:=(ansy+g[i]) mod modd;
    end;
  writeln(ansx);
  writeln(ansy);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值