被 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.