这次day2的比赛感觉难度比day1稍稍大了点(看懂题目和做出题目的难度都大……),语文没学好果然是个问题。看懂了题目之后再看别人提问就觉得无语——题目不是交代了么(虽然不容易看出来)?
T1:二叉树的根
题目大意:给出一个无根树,就是一个有N个点、N-1条边的连通图(N<=100000),问你有哪些点可以作为一棵二叉树的根。
首先如果树上存在度数大于3的点,就没有任何一个点符合条件(选哪个点作根,度数大于3的点都会至少有3个儿子)。其次度数为3的点不能作为树根(选它为树根则它有3个儿子,样例的提示很明显),因此度数为1或2的点就可以作为树根。时间复杂度O(N)。
var
a:array[1..100000] of longint;
n,i,c,x,y:longint;
begin
assign(input,'root.in');reset(input);
assign(output,'root.out');rewrite(output);
readln(n);
c:=n;
for i:=1 to n-1 do begin
readln(x,y);
inc(a[x]);
inc(a[y]);
if (a[x]>3) or (a[y]>3) then begin
writeln(0);
close(input);close(output);
halt;
end;
if a[x]=3 then dec(c);
if a[y]=3 then dec(c);
end;
writeln(c);
for i:=1 to n do if a[i]<3 then write(i,' ');
writeln;
close(input);close(output);
end.
T2:距离统计
题目大意:给出一个N×M的点阵(N、M<=10^9),两两顶点间构成一条线段,问长度为L的线段有多少条(L为整数且保证有解),一共询问T次(T<=1000)。
很明显横向和纵向的线段是可以直接计算出来的,非常简单。但是斜线的话……
这道题比较DT的原因是要解a^2+b^2=L^2,其中a、b为整数,L为已知数。怎么解?朴素的方法是枚举a或b,时间复杂度O(TN)。有什么更好的方法吗?
通过对公式x^2+y^2=z^2(其中gcd(x,y,z)=1)的整理可知,z-y必定为完全平方数或完全平方数的2倍(由于其证明过于高大上,请自行看比赛解题报告中的证明)。因此首先从所有L的约数中枚举gcd(a,b,L),设为p。令z=L/p,此时只要枚举一个数i,那么i^2或者2i^2就有可能是z-y的值,由此推出y与x。最后判断gcd(x,y,z)是否为1即可,如果是,则a=x*p,b=y*p(显然)。
const
maxp=50000;
var
a:array[0..30000] of longint;
f:array[2..maxp] of boolean;
c:array[1..30000,1..2] of longint;
n,m,l,ans:int64;
t,i,j,nc:longint;
procedure add(x,y:int64);
begin
if (x
0) do begin
if a[i]*a[i]>l then break;
while l mod a[i]=0 do begin
inc(nc);
c[nc,1]:=a[i];
c[nc,2]:=0;
while l mod a[i]=0 do begin
inc(c[nc,2]);
l:=l div a[i];
end;
end;
inc(i);
end;
if l>1 then begin inc(nc); c[nc,1]:=l; c[nc,2]:=1; end;
end;
function gcd(a,b:int64):int64; begin if b=0 then exit(a) else exit(gcd(b,a mod b)); end;
procedure check(x:int64);
var
i:longint;
a,b,p:int64;
begin
for i:=1 to x do begin
b:=x-i*i;
if b<=0 then break;
p:=x*x-b*b;
a:=trunc(sqrt(p));
if a>=b then break;
if (a*a=p) and (gcd(x,gcd(a,b))=1) then add(a*(l div x),b*(l div x));
end;
for i:=1 to x do begin
b:=x-i*i*2;
if b<=0 then break;
p:=x*x-b*b;
a:=trunc(sqrt(p));
if a>=b then break;
if (a*a=p) and (gcd(x,gcd(a,b))=1) then add(a*(l div x),b*(l div x));
end;
end;
procedure dg(v:longint;l:int64);
var
i:longint;
begin
if v>nc then check(l)
else
for i:=0 to c[v,2] do begin
dg(v+1,l);
l:=l div c[v,1];
end;
end;
begin
for i:=2 to maxp do begin
if not f[i] then begin inc(a[0]); a[a[0]]:=i; end;
for j:=1 to a[0] do begin
if i*a[j]>maxp then break;
f[i*a[j]]:=true;
if i mod a[j]=0 then break;
end;
end;
assign(input,'dist.in');reset(input);
assign(output,'dist.out');rewrite(output);
readln(n,m,t);
for t:=1 to t do begin
read(l);
ans:=0;
if l
总结:这次比赛中我拿下T1,T2和T3只有20分,一共140分。如上所说,我在理解题目(特别是T3)的时候出现了一些困难,以为T3可能存在混联电路,还打算恶补物理(……)。幸好最后看清题目,知道怎么下手。这次比赛主要存在的问题是数学知识的缺失。T2就是一个典型的例子。我还以为所有的勾股数都可以通过一些自己总结的公式算出来(一些派生勾股数就不行),如果T2老老实实枚举a或b,就可以多拿40分……而T3纯粹是因为程序打错,比赛结束之后我改了两句就AC了,可见检查不够周到。不过庆幸的是读懂了题,没有被文字卡住。可惜比赛时大部分时间都用作推理和实现程序,并没有对拍,这点一定要加倍注意。