首页 » 技术分享 » 雀巢咖啡杯模拟赛?!(一) 倍增啊倍增!!

雀巢咖啡杯模拟赛?!(一) 倍增啊倍增!!

 

    

       被 poj 的数据坑住了,于是乎想到了这个N久没有写的博客了。

       前两天被用来当做是NOIP 模拟题的............雀巢咖啡杯模拟赛题.....(@ . @),考的是相当的萎 ,就当积攒rp,明天爆发吧。

    

        第一天的题目,第一题相当坑人,就是求最大子段积,结果被骗写高精(!。!),写的太丑了,而实际上谢老师挑选的数据水,最后打裸就行了.........

        第二题,无语的贪心,被altman 用微元法解释(=.=),加上边界特判,对的无比诡异.......

        

  ============================================ 以上都是废话==============================================

   

        感觉第三题倍增的思想满喜欢的,题意是给定一个有向图,其中每个节点都有k 条出边,为其编号1~k(K<=26),每条边附带权值。然后给定一串行走指令:假设你初始在1点,给你100000 个操作   Ai  ,Ci, 表示沿着标号为Ai的边走Ci次,让你执行这些操作,并记录经过的边的边权和。 Ci 有10^9级别。

       考场上其实看完题目后就感觉到是倍增之类的东西,但是当时被第一题坑了,写完第一题只有半个小时了(而且第一题还写错了~.~),就没有去想了,当然,就算想了可能也编不出,因为,其实还没有编过倍增。

         .......

       

      于是乎后来发现倍增是多么美妙,多么好写........

       就这道题而言,就是预处理从第i 个节点 沿第j 种边走 2^k条边的情况,最后用预处理的数组裸算就可以了。

       反正就是非常神奇的用了二进制,二进制真神奇(忽略我卖萌.........)


       于是去写了lca 的nlogn 的倍增算法,话说至今还没有写过倍增lca(其实貌似tanjan lca也没写几次,应该被鄙视!.!);

       贴个代码吧:::::

  

program  poj1330;
var
     l,sum,next,d:array[0..30000]of longint;
     g:array[0..10000+10]of boolean;
     k,t,top,i,j,n,m,x,y,dx:longint;
     f:array[0..10000+10,0..15] of longint;
procedure inf;
begin
   assign(input,'1330.in');
   assign(output,'1330.out');
   reset(input);rewrite(output);
end;
procedure ouf;
begin
   close(input);close(output);
end;
procedure origin;
begin
    fillchar(l,sizeof(l),0);
    fillchar(next,sizeof(next),0);
    fillchar(sum,sizeof(sum),0);
    fillchar(f,sizeof(f),0);
    fillchar(g,sizeof(g),false);
    top:=0;
end;
procedure dfs(rt,x,low:longint);
var k:longint;
begin
     k:=l[x];  f[x,0]:=rt;  d[x]:=low;
     if low>m then m:=low;
     while k<>0 do
      begin
        if sum[k]<>rt then dfs(x,sum[k],low+1);
        k:=next[k];
      end;
end;
procedure link(x,y:longint);
begin
     inc(top);
     next[top]:=l[x];
     l[x]:=top;
     sum[top]:=y;
end;
procedure init;
begin
    read(n); origin;
    for i:=1 to n-1 do
        begin
           read(x,y); g[y]:=true;
           link(x,y);
        end;
    for i:=1 to n do
    if g[i]=false then dfs(0,i,1);
    for j:=1 to trunc(ln(m)/ln(2))  do
      for i:=1 to n do
          f[i,j]:=f[f[i,j-1],j-1];
    read(x,y);
end;

procedure main;
begin
     if d[x]<d[y] then begin dx:=x; x:=y; y:=dx; end;
     dx:=d[x]-d[y];
     if dx>0 then
     for i:=0 to trunc(ln(dx)/ln(2)) do
         if (dx shr i) and 1=1 then
            x:=f[x,i];
     k:=0;
     while x<>y do
      begin
        if (f[x,k]<>f[y,k])  or ((f[x,k]=f[y,k])  and (k=0)) then
         begin
           x:=f[x,k]; y:=f[y,k];
           inc(k);
         end
        else dec(k);
      end;
     writeln(x);
end;

begin
  // inf;
   read(t);
   for t:=1 to t do
     begin
      init;
      main;
     end;
  // ouf;
end.                   

写的很丑(不说也发现了.........)

总之就是首先预处理f【i,k】,即i节点往上延伸2^k 层的父亲,然后先用这个数组把询问的x,y 提到相同高度,

 if d[x]<d[y] then begin dx:=x; x:=y; y:=dx; end;
     dx:=d[x]-d[y];
     if dx>0 then
     for i:=0 to trunc(ln(dx)/ln(2)) do
         if (dx shr i) and 1=1 then
            x:=f[x,i];

然后再利用貌似二分一样的东西:

对于同一深度的x,y,如果f【x,k】<>f【x,k】,说明k还不够大,lca还在f【x,k】上;

                                         ****f 【x,k】= f【x,k】,说明k够大了,应该适当减小k

 k:=0;
     while x<>y do
      begin
        if (f[x,k]<>f[y,k])  or ((f[x,k]=f[y,k])  and (k=0)) then
         begin
           x:=f[x,k]; y:=f[y,k];
           inc(k);
         end
        else dec(k);
      end;

就这样了.............


顺便写了tarjan lca:

program ex1330_tarjan;
var
  fa:array[0..20000]of longint;
  g:array[0..20000]of boolean;
  l,sum,next:array[0..30000]of longint;
  x,y,i,n,ans,t,top,k:longint;
procedure inf;
begin
   assign(input,'1330.in');
   assign(output,'1330.out');
   reset(input);rewrite(output);
end;
procedure ouf;
begin
   close(input);close(output);
end;
procedure link(x,y:longint);
begin
     inc(top);
     next[top]:=l[x];
     l[x]:=top;
     sum[top]:=y;
end;
procedure origin;
begin
    fillchar(l,sizeof(l),0);
    fillchar(next,sizeof(next),0);
    fillchar(sum,sizeof(sum),0);
    fillchar(g,sizeof(g),false);
    for i:=1 to n do fa[i]:=i;
    top:=0;
end;
procedure init;
begin
    read(n);
    origin;
    for i:=1 to n-1 do
      begin
        read(x,y); g[y]:=true;
        link(x,y);
      end;
    for i:=1 to n do
     if g[i]=false then k:=i;
    fillchar(g,sizeof(g),false);
    read(x,y);
end;
function find(x:longint):longint;
begin
  if fa[x]<>x then fa[x]:=find(fa[x]);
  exit(fa[x]);
end;
procedure dfs(rt:longint);
var k:longint;
begin
   if rt=5 then
     rt:=rt;
   k:=l[rt]; g[rt]:=true;
   if rt=x then if g[y] then ans:=find(y);
   if rt=y then if g[x] then ans:=find(X);
   while k<>0 do
     begin
       dfs(sum[k]);
       fa[sum[k]]:=rt;
       k:=next[k];
     end;
end;
begin
  // inf;
   read(t);
   for t:=1 to t do
     begin
       init;
       dfs(k);
       writeln(ans);
     end;
   //ouf;
end.                                      

 

转载自原文链接, 如需删除请联系管理员。

原文链接:雀巢咖啡杯模拟赛?!(一) 倍增啊倍增!!,转载请注明来源!

0