继续每天一套模拟题啦啦啦~~~因为昨天的题目难度适中,而且我在比赛时使用的刷水分策略得当,导致我得到了一个非常适合形容自己的分数——222分……
T1:Span
原题:{屏蔽}……PS:以后可能都不会贴原题大图啦,因为有群众反映大图杀猫(……)
题目大意:给出一个包含N个点M条无向边的图(N<=100),要求从M条边中选出N-1条边使得N个点两两相通,求这N-1条边中最长的边与最短的边边权之差的最小值。输入中包含多组数据(T<=6)。
咋一看这道题很像最小生成树,不过题目的要求不是总代价最小,迷惑了很多无知少年……实际上因为N足够小,可以从M条边中枚举一条边作为生成树中的最短边,另外再选N-2条边,算出答案。看起来时间复杂度达到了O(M^2),但实际上并没有这么大(题目数据很水??)(应该是的,我也不清楚)。
var
a,b:array[0..10000,1..3] of longint;
f:array[1..100] of longint;
t,n,m,i,j,k,x,y,p,ans:longint;
function min(x,y:longint):longint; begin if x
=y then exit;
i:=x;
j:=y;
m:=b[random(y-x+1)+x,3];
repeat
while b[i,3]
m do dec(j);
if i<=j then begin
b[0]:=b[i];
b[i]:=b[j];
b[j]:=b[0];
inc(i);
dec(j);
end;
until i>j;
qsort(x,j);
qsort(i,y);
end;
function getf(v:longint):longint; begin if f[v]=v then exit(v) else begin f[v]:=getf(f[v]); exit(f[v]); end; end;
begin
randomize;
assign(input,'span.in');reset(input);
assign(output,'span.out');rewrite(output);
readln(t);
for t:=1 to t do begin
readln(n,m);
for i:=1 to m do readln(b[i,1],b[i,2],b[i,3]);
qsort(1,m);
ans:=maxlongint;
for i:=1 to m do begin
for j:=1 to n do f[j]:=j;
k:=0;
for j:=i to m do begin
x:=getf(b[j,1]);
y:=getf(b[j,2]);
if x<>y then begin
f[y]:=x;
inc(k);
if k=n-1 then break;
end;
end;
if k
T2:无聊的草稿
题目大意:给出一个包含N个点N-1条无向边的连通图(N<=10^6),求图上的任意一条链上的节点个数和与链上节点相邻的节点个数之和的最大值。
看到“链”这个字,加上题目给出来的连通图实际上是一棵树,我们可以想到“树的直径”。但是相邻的节点怎么计数?有个简单的方法——直接将每个点的儿子个数作为这个点的“点权”,如果将“树的直径”中的边权改为点权,那么就相当于做“树的直径”了。
求树的直径,我们可以以任何一个点为起点做最长路,找到与起点最远的节点;再以这个节点为起点做一次最长路,则此时求得的最远距离实际上就是树的直径。
var
a,c:array[1..1000000] of longint;
b:array[0..2000000,1..2] of longint;
n,i,x,y,st,ans:longint;
procedure find(v,p,l:longint);
var
z:longint;
begin
if p>ans then begin ans:=p; st:=v; end;
z:=a[v];
while z>0 do begin
if b[z,1]<>l then find(b[z,1],p+c[b[z,1]]-1,v);
z:=b[z,2];
end;
end;
begin
assign(input,'boring.in');reset(input);
assign(output,'boring.out');rewrite(output);
readln(n);
for i:=1 to n-1 do begin
readln(x,y);
b[i*2-1,1]:=y;
b[i*2-1,2]:=a[x];
a[x]:=i*2-1;
b[i*2,1]:=x;
b[i*2,2]:=a[y];
a[y]:=i*2;
inc(c[x]);
inc(c[y]);
end;
ans:=0;
find(1,c[1]+1,0);
find(st,c[st]+1,0);
writeln(ans);
close(input);close(output);
end.
T3:锻炼身体
原题:NOI2005“瑰丽华尔兹”数据弱化版
题目大意:有一个N×M的迷宫,有些点是障碍。给出K个时间段,从起点出发,每个时间段中只能按照该时间段对应的方向行走或原地不动,求K个时间段后最长的行走距离。
又是迷宫题……果断BFS搞起……实际上除了万能的BFS,可以设DP方程F[i,j,T]表示走完前T个时间段走到(i,j)的最长行走距离。时间复杂度看起来是O(NMK*Step),其中Step为一个时间段中能行走的最长距离。但因为K*Step实际值较小,所以仍然可以AC(据说连作为此题数据加强版的“瑰丽华尔兹”也能过)。
const
fx:array[1..4,1..2] of longint=((-1,0),(1,0),(0,-1),(0,1));
maxd=2000000;
var
a:array[0..201,0..201] of boolean;
b:array[0..200,1..3] of longint;
f:array[0..201,0..201,1..4] of longint;
d:array[1..maxd,1..3] of longint;
p:array[1..200,1..200,0..200] of longint;
n,m,c,i,j,k,ans,x,y,t:longint;
ch:char;
function min(x,y:longint):longint; begin if x
b[j,1] then begin
b[0]:=b[i];
b[i]:=b[j];
b[j]:=b[0];
end;
i:=0;
j:=1;
ans:=0;
while i<>j do begin
i:=i mod maxd+1;
if d[i,3]=c then break;
t:=d[i,3]+1;
k:=min(f[d[i,1],d[i,2],b[t,3]],b[t,2]-b[t,1]+1);
for k:=0 to k do begin
x:=d[i,1]+fx[b[t,3],1]*k;
y:=d[i,2]+fx[b[t,3],2]*k;
if p[x,y,t]
ans then ans:=p[x,y,t];
end;
end;
end;
writeln(ans);
close(input);close(output);
end.
T4:Train
题目大意:有N个车站按编号顺序排成一行(N<=200),有一列列车会依次通过这N个车站。有M节车厢(M<=200),每个车厢有一个起始车站和终点车站,每节车厢在起始车站接上列车并在终点车站从列车上卸下。车厢只能在列车的头或尾接上,也只能在这两端被卸下。求一个合法的操作序列。
给这题跪了……虽然这道题很像NOIP2008“双栈排序”,但区别在于这道题是一个双头队列,不是两个栈……根据解题报告,正解是枚举每节车厢接上列车的哪一侧(即搜索)加上剪枝。不过由于报告的作者表达能力不佳,导致连moreD神犇都不知道报告究竟想说什么……因此无人能解……
总结:T1我用上了错误的方法但得到了70分,T2暴力40分,T3顺利过关,T4打无解的情况怒刷12分(无视之)……不得不说做这套题简直就像是在做一场“搜索专题训练”……各种暴力……这不得不提醒我:实际上正解很有可能就是暴力的时间空间优化版,不能小看暴力的威力啊!另外因为在做T3时打上了对拍找到了Bug,因此顺利地A掉T3,感觉那是相当的好……