被 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.
转载自原文链接, 如需删除请联系管理员。
原文链接:雀巢咖啡杯模拟赛?!(一) 倍增啊倍增!!,转载请注明来源!