【Nov 10,哈希与并查集】模拟赛题解

陌路散爱 提交于 2020-01-18 02:40:35

      学习炜哥,最后再发代码。

 

No.1 distinct

 

【问题描述】     陶陶为了给一道平面几何题出数据,需要产生 N 个点(x[i],y[i])。已知 x,y 是由伪随机函数顺序产生,即:   X[i+1] = (X[i]*Ax+Bx+i) mod Cx  (X[1], Ax,Bx,Cx 是事先给定的)   Y[i+1] = (Y[i]*Ay+By+i) mod Cy  (Y[1], Ay,By,Cy 是事先给定的)     这样,就可以快速连续产生很多点坐标(X[i], Y[i])。     不幸的是,这样产生的点有可能有相同的,虽然这种几率很少,但严谨的陶陶不允许这种事发生。陶陶要求你帮助他解决最少要产生前多少项时,正好有  N  个不相同的点。 【输入格式】第一行。一个整数  N . 第二行:4 个整数  X[1]、  Ax、Bx、Cx . 第三行:4 个整数  Y[1]、  Ay、By、Cy . 【输出格式】 一个整数  M  。表示最少要连续产生  M  个点,正好有  N  个不相同的点。数据保证有答案。  【数据范围】1<=N<=1,000,000,  其它所有数据都在[1...1,000,000,000]范围内。 【输入样例】21 2 4 3 6 5 2 3 13 【输出样例】 24

 

很单纯的Hash。将求出的x与y分别成一个大质数,加起来模一个大质数即可。

 

No.2 Allbarns

 

【题目描述】    农民约翰打算建一个新的矩形谷仓。但是,矩形谷仓的 4 个角落不能在落在软土路基上,只能落在一些固定点上。现在,他已经找到地面上有 N(4 <= N <= 1,000)个点,角落只可以落在这些点上。他想知道依次每加多一个点,可以建立新谷仓的方法数量,请你帮助他找到答案。 【输入格式】     第 1 行:一个整数,N     第 2 行至 N +1 行:每行有两个被空格分隔的整数的 x,y,作为一个点的坐标。所有的 x,y 都不会超过 16,000。所有点都是不同的。 【输出格式】共N行:每行表示当前可以建立的新的谷仓的数目。 【样例输入】8 1 2 1 -2 2 1 2 -1 -1 2 -1 -2 -2 1 -2 -1 【样例输出】0 0 0 0 0 1 3 6 【注释】最后的答案是(1,2,6,5),(1,3,6,8),(1,4,6,7),(2,3,5,8),(2,4,5,7), (3,4,8,7)

 

      还是哈希。对边进行hash,分别用边的中点横纵坐标和边长乘不同的大质数,再模一个大质数。由矩形性质,两条hash值相同的不同边即可构成一个矩形。

 

No.3 Cubes

 

【题目描述】    FJ 和 Best 用  N (1 <= N <= 30,000)块相同的小立方块玩游戏,小方块编号为1..N。开始时,小方块都单独分开的,每个看成一个柱子,即有N柱子。 FJ 要 Best 做P(1 <= P <=  100,000)个操作,操作有两种类型:   (1) FJ 要求 Best 把X 号方块所在的柱子放到 Y 号所在的柱子上面,成一个新柱子。   (2)FJ 要求 Best 计算X 号方块所在柱子,它下面有多少个小方块。     请编个程序,帮助 Bet 计算。 【输入格式】    第一行:一个整数  P     第 2..P+1 行:第 i+1 行表示第 i 个 FJ 要求的合法操作。如果这行以'M'开头,后面有两个整数  X,y  表示要进入(1)操作。  如果这行以'C'开头,后面有一个整数X,表示要求计算 X 所在柱子下面的方块个数。     注:所有操作都是合法的。N  并没有出现在输入文件中。 【输出格式】 依次要求计算的值,每次一行。 【输入样例】 6           | 6 个操作 M 1 6       | 1,6 / 2 / 3 / 4 / 5 把 1 放在 6 上面。 C 1         | 输出:1 M 2 4       | 1,6 / 2,4 / 3 / 5 M 2 6       | 2,4,1,6 / 3 / 5 C 3         | 输出 :0 C 4         | 输出: 2 【输出样例】1 0 2


      并查集。这道题和NOI2002年的银河英雄传说几乎相同…在这里不再赘述方法,网上很多。


 

No.4 friend

 

【问题描述】     有一个镇有 N 个居民。当然其中有许多人是朋友的关系。根据有名的谚语:“我朋友的朋友也是我的朋友”,所以如果 A 和 B 是朋友,B 和 C 是朋友,那么 A 和 C 也是朋友。 你的任务是算出在这个镇中最大的朋友集团为多少人。 【输入文件】     输入文件的第一行有 2 个正整数 N 和 M 。N 代表镇上居民的数目(1<=N<= 30000),M 代表这些居民中朋友关系的数目( 0 <= M <= 30000)。接下来的 M 行每行有 2 个整数A,B( 1 <= A,B <= N , A 不等于 B),代表 A,B 为朋友关系。这 M 行中可能有的会重复出现。  【输出文件】 输出文件仅一行,在这个镇中最大的朋友集团为多少人。  【输入样例】 10 12 1 2 3 1 3 4 5 4 3 5 4 6 5 2 2 1 7 10 1 2 9 10 8 9 【输出样例】 6 


      很纯粹的并查集。注意在计数前再压缩一下路径。


参考代码(distinct):

 

program distinct;
  var
    xx,yy:array[1..10000000]of longint;
    v:array[1..10000000]of boolean;
    num,x,y,i:int64;
    n,m:longint;
    q2,q1,a1,a2,b1,b2,c1,c2:longint;
  begin
    readln(n);
    readln(x,a1,b1,c1);
    readln(y,a2,b2,c2);
    m:=0;
    while m<n do
      begin
        inc(i);
        num:=(x*499979+y*299993)mod 9999991;
        while(v[num])and((xx[num]<>x)or(yy[num]<>y))do
          num:=(num+9871)mod 9999991;
        if not v[num]then
          begin
            yy[num]:=y;
            xx[num]:=x;
            v[num]:=true;
            inc(m);
          end;
        x:=(x*a1+b1+i)mod c1;
        y:=(y*a2+b2+i)mod c2;
      end;
    writeln(i);
  end.

 

参考代码(allbarns):

 

program allbarns;
  var
    x,y:longint;
    hash,len,xx,yy:array[1..4000037]of longint;
    nx,ny:array[1..4000037]of longint;
    n:integer;
    i,j:integer;
    h,l:int64;
    a,b,ans:longint;
  begin
    readln(n);
    for i:=1 to n do
      begin
        readln(nx[i],ny[i]);
        for j:=1 to i-1 do
          begin
            a:=(nx[i]+nx[j]);
            b:=(ny[i]+ny[j]);
            l:=sqr(nx[i]-nx[j])+sqr(ny[i]-ny[j]);
            h:=(abs(a*5869+b*3217+l*6529))mod 4000037;
            while(hash[h]<>0)and((len[h]<>l)or(xx[h]<>a)or(yy[h]<>b))do
              h:=(h+9871)mod 4000037;
            if hash[h]=0 then
              begin
                xx[h]:=a;
                yy[h]:=b;
                len[h]:=l;
                hash[h]:=1;
              end
              else begin
                     ans:=ans+hash[h];
                     inc(hash[h]);
                   end;
          end;
        writeln(ans);
      end;
  end.

 

参考代码(cubes):

 

program cubes;
  var
    before,count,f:array[1..30000]of integer;
    i,j,m,n,x,y,max:longint;
    c:char;
  function getfather(x:longint):longint;
    var
      fa:longint;
    begin
      if x=f[x] then exit(x);
      fa:=getfather(f[x]);
      before[x]:=before[x]+before[f[x]];
      f[x]:=fa;
      exit(f[x]);
    end;
  procedure union(x,y:longint);
    begin
      x:=getfather(x);
      y:=getfather(y);
      f[x]:=y;
      before[x]:=before[x]+count[y];
      count[y]:=count[x]+count[y];
    end;
  begin
    readln(n);
    for i:=1 to 30000 do
      begin
        f[i]:=i;
        before[i]:=0;
        count[i]:=1;
      end;
    for i:=1 to n do
      begin
        read(c);
        case c of
          'M':begin
                readln(x,y);
                if f[x]<>f[y] then
                  union(x,y);
              end;
          'C':begin
                readln(x);
                getfather(x);
                writeln(before[x]);
              end;
        end;
      end;
  end.

 

 

参考代码(friend):

 

program friend;
  var
    f:array[1..30000]of integer;
    i,j,m,n,x,y,max:longint;
    ans:array[1..30000]of integer;
  function getfather(x:longint):longint;
    begin
      if x=f[x] then exit(x);
      f[x]:=getfather(f[x]);
      exit(f[x]);
    end;
  procedure union(x,y:longint);
    begin
      x:=getfather(x);
      y:=getfather(y);
      if x<>y then f[x]:=y;
    end;
  begin
    readln(n,m);
    for i:=1 to n do
      f[i]:=i;
    for i:=1 to m do
      begin
        readln(x,y);
        if f[x]<>f[y] then
          union(x,y);
      end;
    for i:=1 to n do
      f[i]:=getfather(i);
    max:=0;
    for i:=1 to n do
      inc(ans[f[i]]);
    for i:=1 to n do
      if ans[i]>max then max:=ans[i];
    writeln(max);
  end.

 

 

 

本文地址:http://www.cnblogs.com/saltless/archive/2010/11/10/1874126.html

(saltless原创,转载请注明出处)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!