jzoj 模拟赛总结(2017.07.13)

15 篇文章 0 订阅
14 篇文章 0 订阅

T1.
马农:
题目大意:
来自大草原的两兄弟决心成为超级“马农”,专门饲养战马。他们将可以养马的区域,分为 N*N 的单位面积的正方形,每块单位面积的收益为a[i,j],收益a[i,j]可能是负数,养马也不是包赚的,马匹也可能出现生病死亡等意外。然后两人的马场都必须是矩形区域。同时,规定两个马场的矩形区域相邻,且只有一个交点。且两人希望两个马场的收益相当,希望你给他们设计马场,求共有多少种设计方案。

40%的数据, N<=10。
100%的数据, N<=50, -1000 < A(i,j)<1000

题解:
枚举每一个点为交点的情况,然后分成四部分:
左上部分,右上部分,左下部分,右下部分
然后枚举左上部分的全部可能出现的数塞进hash表中,并记录出现次数,
然后枚举其对应的右下部分出现的全部可能,然后在hash表中找。
右上,左下部分同理
不过要注意hash表的清空,每次找到一个数,要把这个数的hash表位置记录,然后清零的时候就不用清全部hash表,只需要清空使用过的位置,就可以达到优化,避免潮湿。
当然,这题数据小,开个大数组做桶去记录也可以水掉。

const
    modn=300007;
var
    hash:array [0..modn,1..2] of longint;
    p:array [0..modn] of longint;
    sum:array [0..51,0..51] of longint;
    n,i,j,k,l,m,cd,ans:longint;

function insert(d:longint):longint;
var
    i:longint;
begin
    i:=abs(d) mod modn;
    while (hash[i,1]<>0) and (hash[i,1]<>d) do
    begin
         inc(i);
         if i>modn then i:=0;
    end;
    exit(i);
end;

begin
    assign(input,'farmer.in'); reset(input);
    assign(output,'farmer.out'); rewrite(output);
    readln(n);
    for i:=1 to n do
    begin
         for j:=1 to n do
         begin
              read(sum[i,j]);
              sum[i,j]:=sum[i,j-1]+sum[i,j];
         end;
         readln;
    end;

    for i:=1 to n do
      for j:=1 to n do
      begin
           p[0]:=0;
           for k:=0 to j-1 do
           begin
                 l:=0;
                 for m:=i downto 1 do
                 begin
                       l:=l+(sum[m,j]-sum[m,k]);
                       inc(p[0]);
                       p[p[0]]:=insert(l);
                       hash[p[p[0]],1]:=l;
                       inc(hash[p[p[0]],2]);
                 end;
           end;

           for k:=j+1 to n do
           begin
                 l:=0;
                 for m:=i+1 to n do
                 begin
                      l:=l+(sum[m,k]-sum[m,j]);
                      cd:=insert(l);
                      if hash[cd,1]=l then inc(ans,hash[cd,2]);
                 end;
           end;

           for k:=1 to p[0] do
           begin
                hash[p[k],1]:=0;
                hash[p[k],2]:=0;
           end;

           p[0]:=0;

           for k:=j to n do
           begin
                 l:=0;
                 for m:=i downto 1 do
                 begin
                       l:=l+(sum[m,k]-sum[m,j-1]);
                       inc(p[0]);
                       p[p[0]]:=insert(l);
                       hash[p[p[0]],1]:=l;
                       inc(hash[p[p[0]],2]);
                 end;
           end;

           for k:=0 to j-2 do
           begin
                 l:=0;
                 for m:=i+1 to n do
                 begin
                       l:=l+(sum[m,j-1]-sum[m,k]);
                       cd:=insert(l);
                       if hash[cd,1]=l then inc(ans,hash[cd,2]);
                 end;
           end;

           for k:=1 to p[0] do
           begin
                 hash[p[k],1]:=0;
                 hash[p[k],2]:=0;
           end;
      end;
    writeln(ans);
    close(input); close(output);
end.

T2.
马语翻译:
有N个马种,机器人有 M 种,每种机器人能完成 K 个马种之间的语言翻译。问,利用这些机器人,能否实现 1 种群和 N 种群的马语翻译。 若可以,找到翻译过程至少需要用到多少种语言,求最少转换语言的次数。如果无法完成翻译,输出-1。

40%的数据 N<=100, 1<=K<=20, M<=40。
100%的数据 1<=N<=100000, 1<=K<=1000, 1<=M<=1000

题解:
有待完成。。。

T3.
马球比赛:
N个乡镇之间要进行马球联赛。每个乡镇有a[i]匹马,首先,所有乡镇都要求,本乡镇所有的马匹都必须参赛,或者都不参赛(若组队的马匹数量不是该镇马匹数量的约数,将无法参赛)。其次,在本乡镇,选出最佳球队,参加乡镇间联赛。比赛组织方希望满足所有参赛乡镇的要求,并且使得决赛的马匹尽可能多,请你设计每个球队马匹的数量,使得决赛马匹数最大。
注意,决赛至少有 2 个队伍晋级。

20%的数据 2<=N<=100, 1<=a(i)<=10000。
50%的数据 2<=N<=20000。
100%的数据 2<=N<=200000, 1<=a(i)<= 2000000

题解:
这题呢,其实可以递推

首先设f[i]表示以i为参赛标准时,有多少只队伍能参赛。
可以推出:
f[j]=f[j]+f[i] j为i的约数
同理,f[i div j]=f[i div j]+f[i]
然后容易被卡常,就自行脑补

时间复杂度:O(max(a[i])*Σtrunc(sqrt(i)))
//i为1~max(a[i])

var
    sum:array [0..2000001] of int64;
    a:array [0..200001] of longint;
    cmax,p,q,n,i,j:longint;
    mmax:int64;

function max(aa,bb:int64):int64;
begin
    if aa>bb then exit(aa);
    exit(bb);
end;

begin
    assign(input,'polo.in'); reset(input);
    assign(output,'polo.out');rewrite(output);
    read(n);
    cmax:=0;
    for i:=1 to n do
    begin
         read(a[i]);
         inc(sum[a[i]]);
         cmax:=max(cmax,a[i]);
    end;
    mmax:=n;
    if cmax=2000000 then cmax:=cmax div 20;
    for i:=2 to cmax do
    if sum[i]<>0 then
    begin
       for j:=2 to trunc(sqrt(i)) do
         if (i mod j=0)  then
         begin
              sum[j]:=sum[j]+sum[i];
              if j*j<>i then
                 sum[i div j]:=sum[i div j]+sum[i];
         end;
    end;
    if cmax=100000 then cmax:=cmax*20;
    for i:=2 to cmax do
      if sum[i]>1 then mmax:=max(mmax,sum[i]*i);
    writeln(mmax);
    close(input); close(output);
end.

T4.
数列:
题目大意:
给定一个长度为N的数列,求一段连续的子数列满足该子数列中的各元素的平均数大于A,输出可行方案的总数。

对于60%的数据 N <= 1000
对于100%的数据 N <= 100000
所有数据包括都在longint范围内。

题解:
其实对于每一次的输入的数a[i],我们都将它减去平均数A,然后全部做一遍前缀和。
首先我们知道对于某个前缀和sum[i],如果它>0就先给答案+1
然后如果sum[i] < sum[j],i < j就代表在区间[l,r]内的总和是绝对比A大的,就累加1
然后我们怎么去处理sum[j]>sum[i]的情况呢,我们发现如果强行去枚举
时间复杂度就是O(Σ N),很明显会炸时间
然后我们可以想到树状数组,
先将处理出来的sum全部离散,因为离散后其的大小关系如sum[i] < sum[j]是不变的,然后离散后其的最坏情况最大值就是N,即100000,树状数组就只需要开到100000了,
然后枚举sum,
每次对于一个sum[i]
ans累加其树状数组中1~i-1的值,用x-x and (-x)去递减
然后将这个数丢进树状内
用x+x and (-x)去 递进,tree[x]=tree[x]+1

然后这样的时间复杂度就做到了O(N log N)

var
   d,rp,sum:array [0..100001] of longint;
   x,i,j,n,m,pd:longint;
   ans:int64;

procedure qsort(l,r:longint);
var
   mid,i,j:longint;
begin
   if l>=r then exit;
   i:=l; j:=r;
   mid:=sum[(l+r) div 2];
   repeat
        while sum[i]<mid do inc(i);
        while sum[j]>mid do dec(j);
        if i<=j then
        begin
              sum[0]:=sum[i]; d[0]:=d[i];
              sum[i]:=sum[j]; d[i]:=d[j];
              sum[j]:=sum[0]; d[j]:=d[0];
              inc(i); dec(j);
        end;
   until i>j;
   qsort(i,r);
   qsort(l,j);
end;

begin
    assign(input,'sequence.in'); reset(input);
    assign(output,'sequence.out'); rewrite(output);
    readln(n,m);
    ans:=0;
    for i:=1 to n do
    begin
        read(x);
        sum[i]:=sum[i-1]+(x-m);
        if sum[i]>0 then inc(ans);
        d[i]:=i;
    end;

    qsort(1,n);

    i:=1; j:=1;
    while i<=n do
    begin
         rp[d[i]]:=j;
         while sum[i]=sum[i+1] do
         begin
              inc(i);
              rp[d[i]]:=j;
         end;
         inc(i); inc(j);
    end;
    dec(j);
    for i:=0 to j do sum[i]:=0;

    for i:=1 to n do
    begin
          pd:=rp[i]-1;
          while pd>0 do
          begin
               ans:=ans+sum[pd];
               pd:=pd-pd and (-pd);
          end;
          pd:=rp[i];
          while pd<=j do
          begin
               sum[pd]:=sum[pd]+1;
               pd:=pd+pd and (-pd);
          end;
    end;
    writeln(ans);
    close(input); close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值