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