【CH Round #48 - Streaming #3】比赛题解 & 总结

  前天完成的比赛今天才做总结……是不是太迟了 - -!

  不过也清楚自己的题目量不是很多,OI路很长,慢慢刷题吧!这是我第一次来Contest Hunter(CH)做题,以后应该还会做不少比赛吧!

 

  题目:【传送门 http://contesthunter.org/contest/CH%20Round%20%2348%20-%20Streaming%20%233%20(NOIP%E6%A8%A1%E6%8B%9F%E8%B5%9BDay1)】

 

  T1:数三角形

  题目大意:给出平面上N个点(N<=100)的坐标,求这些点能够组成的三角形的个数。

  由于N<=100,所以可以想到枚举三个点判断是否共线的暴力方法,O(N^3)。至于怎么判断三点是否共线,可以各种乱搞(叉积、直线方程……)。

  更进阶的,我们可以考虑N<=1000的情况,此时O(N^3)的算法就不管用了。怎么做呢?可以以任意一个点为原点做极角排序,此时只需O(N)的时间,用叉积判断原点与相邻两点是否共线即可。这样做的总时间复杂度为O(N^2logN)。

 

var
    a:array[1..100,1..2] of longint;
    n,i,j,k,ans,x1,y1,x2,y2,t:longint;
    p:boolean;
function gcd(a,b:longint):longint; begin if b=0 then exit(a) else exit(gcd(b,a mod b)); end;
begin
    assign(input,'triangle.in');reset(input);
    assign(output,'triangle.out');rewrite(output);
    readln(n);
    for i:=1 to n do readln(a[i,1],a[i,2]);
    ans:=0;
    for i:=1 to n do
        for j:=i+1 to n do
            for k:=j+1 to n do begin
                p:=true;
                if ((a[i,1]=a[j,1]) and (a[i,1]=a[k,1])) or ((a[i,2]=a[j,2]) and (a[i,2]=a[k,2])) then p:=false
                else begin
                    x1:=a[j,1]-a[i,1];
                    y1:=a[j,2]-a[i,2];
                    t:=gcd(abs(x1),abs(y1));
                    x1:=x1 div t;
                    y1:=y1 div t;
                    x2:=a[k,1]-a[i,1];
                    y2:=a[k,2]-a[i,2];
                    t:=gcd(abs(x2),abs(y2));
                    x2:=x2 div t;
                    y2:=y2 div t;
                    if ((x1=x2) and (y1=y2)) or ((x1=-x2) and (y1=-y2)) then p:=false;
                end;
                if p then inc(ans);
            end;
    writeln(ans);
    close(input);close(output);
end.


  T2:4和7

  题目大意:给出数轴上的N个点(N<=100000,0<坐标<=10^9),每个点代表一个数A[i](A[i]<=10000),从原点开始每次可以+4或+7到下一个点,并取走该点代表的数,求最大能取到的和。

  首先当然是对所有的点按照坐标排序- -。可以设DP方程F[i]表示前i个点能取到的最大的数,就有F[i] = F[j] + a[i]。由于>17的数都能分成若干个4和7的和,于是枚举j,当i与j的坐标差<=17时暴力判断是否能由4和7组成(扳手指算算……),坐标差>17时F[0..j]的值取最大值计算就好,此时可以维护一个g[i]表示F[0..i]中的最大值,怎么维护……不用再说了吧。

var
    a:array[0..100000,1..2] of longint;
    f,g:array[0..100000] of longint;
    n,i,j,ans:longint;
function max(x,y:longint):longint; begin if x>y then exit(x) else exit(y); end;
procedure qsort(x,y:longint);
    var
        i,j,m:longint;
    begin
        if x>=y then exit;
        i:=x;
        j:=y;
        m:=a[random(y-x+1)+x,1];
        repeat
            while a[i,1]m do dec(j);
            if i<=j then begin
                a[0]:=a[i];
                a[i]:=a[j];
                a[j]:=a[0];
                inc(i);
                dec(j);
            end;
        until i>j;
        qsort(x,j);
        qsort(i,y);
    end;
begin
    randomize;
    assign(input,'medic.in');reset(input);
    assign(output,'medic.out');rewrite(output);
    readln(n);
    for i:=1 to n do readln(a[i,2],a[i,1]);
    qsort(1,n);
    i:=1;
    while i
 

  T3:反射镜

  题目大意:在平面上有N面能将光线反射90°的镜子(N<=100000,坐标绝对值<=10^9),一束光线从原点向x轴正方向射出,求经过T格后光线所在的位置(T<=10^18)。

  由于N不小,T相当大,单纯的模拟复杂度会大得惊人,于是我们需要使用“穿越”的方法,让光线从一面镜子直接到下一面镜子,省略中间光线传播的路程(……),因此我们需要一个O(1)的方法判断一面镜子上下左右分别是哪面镜子——很自然地可以想到对所有镜子按照横坐标和纵坐标分别排序就可以了- -。时间复杂度会降低很多。

  当然,这题有个坑人的地方,那就是光线可能会走环!!我一开始就没有想到光线会走环,于是耿直的拿了85分,剩下3个点超时……什么情况下会走环?经过仔细推理,突然想到一种情况:

 

  也就是当光线向右经过原点时会出现环,因此只要特殊判断光线向右经过原点的情况就好。时间复杂度不好估计,但肯定能过。

var
    a,b:array[-1..100000,1..3] of longint;
    p,q:array[-1..100000] of longint;
    ch:array[0..100000] of char;
    n,m,i,x,y,k,z,l,r,md:longint;
    t,lst:int64;
procedure qsort1(x,y:longint);
    var
        i,j,p,q:longint;
    begin
        if x>=y then exit;
        i:=x;
        j:=y;
        p:=random(y-x+1)+x;
        q:=a[p,2];
        p:=a[p,1];
        repeat
            while (a[i,1]

p) or ((a[j,1]=p) and (a[j,2]>q)) do dec(j); if i<=j then begin a[-1]:=a[i]; a[i]:=a[j]; a[j]:=a[-1]; inc(i); dec(j); end; until i>j; qsort1(x,j); qsort1(i,y); end; procedure qsort2(x,y:longint); var i,j,p,q:longint; begin if x>=y then exit; i:=x; j:=y; p:=random(y-x+1)+x; q:=b[p,2]; p:=b[p,1]; repeat while (b[i,2]q) or ((b[j,2]=q) and (b[j,1]>p)) do dec(j); if i<=j then begin b[-1]:=b[i]; b[i]:=b[j]; b[j]:=b[-1]; inc(i); dec(j); end; until i>j; qsort2(x,j); qsort2(i,y); end; procedure changefx(var k:longint;c:char); begin case c of '\':case k of 1:k:=3; 2:k:=4; 3:k:=1; 4:k:=2; end; '/':case k of 1:k:=4; 2:k:=3; 3:k:=2; 4:k:=1; end; end; end; procedure writeans(x,y:int64); begin writeln(x,' ',y); close(input);close(output); halt; end; begin randomize; assign(input,'mirror.in');reset(input); assign(output,'mirror.out');rewrite(output); readln(n,m,lst); for i:=1 to n do begin readln(a[i,1],a[i,2],ch[i],ch[i]); a[i,3]:=i; b[i]:=a[i]; end; qsort1(1,n); qsort2(0,n); for i:=1 to n do p[a[i,3]]:=i; for i:=0 to n do q[b[i,3]]:=i; x:=0; y:=0; l:=1; r:=n; while l<=r do begin md:=(l+r) shr 1; if b[md,2]y then r:=md-1 else if b[md,1]<=x then l:=md+1 else r:=md-1; end; z:=b[l-1,3]; k:=4; t:=lst; while true do begin l:=1; r:=n; case k of 1: begin {while l<=r do begin md:=(l+r) shr 1; if a[md,1]x then r:=md-1 else if a[md,2]<=y then l:=md+1 else r:=md-1; end;} l:=p[z]+1; if (a[l,1]=x) and (a[l,2]>y) then begin lst:=lst-(a[l,2]-y); if lst<=0 then writeans(x,lst+a[l,2]); y:=a[l,2]; z:=a[l,3]; changefx(k,ch[a[l,3]]); end else writeans(x,lst+y); end; 2: begin {while l<=r do begin md:=(l+r) shr 1; if a[md,1]x then r:=md-1 else if a[md,2]>=y then r:=md-1 else l:=md+1; end; dec(l);} l:=p[z]-1; if (a[l,1]=x) and (a[l,2]y then r:=md-1 else if b[md,1]>=x then r:=md-1 else l:=md+1; end; dec(l);} l:=q[z]-1; if (b[l,2]=y) and (b[l,1]y then r:=md-1 else if b[md,1]<=x then l:=md+1 else r:=md-1; end;} l:=q[z]+1; if (b[l,2]=y) and (b[l,1]>x) then begin lst:=lst-(b[l,1]-x); if (b[l,1]=0) and (y=0) then lst:=lst mod (t-lst); if lst<=0 then writeans(lst+b[l,1],y); x:=b[l,1]; z:=b[l,3]; changefx(k,ch[b[l,3]]); end else writeans(lst+x,y); end; end; end; end.

 

  总结:比赛时T1轻松过掉了,但T2想得不周到,程序出现了很大的漏洞;T3同样是考虑不周到(没有考虑环的情况),丢了15分,最后只有195分。比赛时多读几次题,考虑一些特殊情况很有必要,可以少丢分或者刷水分。当然,对拍同样重要,拍可以检验很多问题,只要拍不难写,为了保证题目得分率,拍到底!最后程序实现的效率也有待提高,实现T3花的时间太多也是影响我用来考虑特殊情况的时间的一个因素。下次还要多多注意这3个方面啦!

你可能感兴趣的:(总结,题解)