jzoj 模拟赛总结(2017.07.08)

T1:
题目大意:
定义 GPT = GPA ×已修学分数。给出系里面每位同学的 GPT(只有一位小数),以及他们的已修学分。求排名 第 K 位的同学的 GPA 。

对于 100% 的数据:
1 ≤ K ≤ N ≤ 100000,GPT 小数点后至多 1 位,GPA 至多 4.0。
所有同学的学分都在 [1, 250] 的范围

题解:
因为知道 GPT = GPA ×已修学分数,所以我们可以发现GPA=GPT/已修学分,然后求出以后快排,注意从大到小,最后输出第K大。

时间复杂度:O(n)

var
    a:Array [0..100001] of real;
    n,k,i,j:longint;
    x,y:real;

procedure qsort(l,r:longint);
var
    i,j:longint;
    mid:real;
begin
    if l>=r then exit;
    i:=l; j:=r;
    mid:=a[(l+r) div 2];
    repeat
         while a[i]>mid do inc(i);
         while a[j]<mid 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(i,r);
    qsort(l,j);
end;

begin
   assign(input,'sort.in'); reset(input);
   assign(output,'sort.out'); rewrite(output);
    readln(n,k);
    for i:=1 to n do
    begin
         readln(x,y);
         a[i]:=x/y;
    end;
    qsort(1,n);
    writeln(a[k]:0:2);
    close(input);close(output);
end.

T2:
题目大意:
给出N个数a[i],以及M个区间查询[l,r]即求出a[l]~a[r]的总和Σ。

对于 50% 的数据:1 ≤ N, M ≤ 100。
对于 100% 的数据:1 ≤ N,M ≤ 100000,0 ≤ Ai ≤ 10000,1 ≤ Li ≤ Ri ≤ N。

题解:
不难发现用前缀和O(N)随便搞搞,然后O(1)求解。
时间复杂度:O(N*M)

var
   sum:array [0..1000001] of longint;
   l,r,i,n,m:longint;
begin
   assign(input,'sum.in'); reset(input);
   assign(output,'sum.out'); rewrite(output);

   readln(n,m);
   for i:=1 to n do
   begin
      read(sum[i]);
      sum[i]:=sum[i]+sum[i-1];
   end;
   readln;

     for i:=1 to m do
     begin
         readln(l,r);
         writeln(sum[r]-sum[l-1]);
     end;

   close(input); close(output);
end.

T3:
递推:
求从(1,1)走到(N,M)积分为P的方案,每次只能向下或者向右走,每个点的积分为1~10的整数a[i,j],起点跟终点的积分也算。

递推式为f[i,j,k]=f[i,j,k]+f[i-1,j,k-a[i,j]]+f[i,j-1,k-a[i,j]]
初值:
f[i,1,Σa[i,1]]=1
f[1,i,Σa[1,i]]=1

相信大家会有一种错误的感觉,P最大是N*m*10即100*100*10,然后就P=100000,数组开到100*100*10000,费内存,浪费时间。
实则我们自己推理可以发现,到每个点(i,j),只需要走(i-1)+(j-1)步,则(N,M)最大为(100,100)时,P最大为10*((100-1)+(100-1)),而远远没有100*100*10,这时我们就可以将数组的P开成2000。
当然大家如果觉得不够优美可以将N滚动滚动。
时间复杂度:O(NMP)

var
    f:Array [0..101,0..101,0..2001] of longint;
    a:array [0..101,0..101] of longint;
    k,i,j,n,m,p:longint;
begin
    assign(input,'count.in'); reset(input);
    assign(output,'count.out');rewrite(output);
    readln(n,m,p);
    for i:=1 to n do
    begin
        for j:=1 to m do read(a[i,j]);
        readln;
    end;
    j:=0;
    for i:=1 to m do
    begin
         j:=j+a[1,i];
         f[1,i,j]:=1;
    end;
    j:=0;
    for i:=1 to n do
    begin
         j:=j+a[i,1];
         f[i,1,j]:=1;
    end;

    for i:=2 to n do
      for j:=2 to m do
        for k:=0 to p-a[i,j] do
        f[i,j,k+a[i,j]]:=(f[i-1,j,k]+f[i,j-1,k]) mod 1000000007;
    writeln(f[n,m,p]);
    close(input);close(output);
end.

T4:
题目大意:
小x有n个小姊妹(根据典故,我们假设n≤3000)。每个小姊妹有自己的名字,他觉得小姊妹的魅力和她们的名字有密切联系,于是他觉得所有有相似的名字的小姊妹必须排在一起。
相似是指,名字的开头一个或若干个连续字母相同。
于是,小x定下了如下规则:
在任何以同样的字母序列开头的名字之间,所有名字开头必须是同样的字母序列。
比如,像MARTHA和MARY这两个名字,它们都以MAR开头,所以像MARCO或MARVIN这样的名字可以插入这两个名字中间,而像MAY这样的就不行。
显然,按字典序排序是一个合法的排序方案,但它不是唯一的方案。你的任务就是计算出所有合法的方案数。考虑到答案可能很大,输出答案 mod 1 000 000 007。

对于60%的数据:3 ≤ n ≤ 10。
对于100%的数据:
3 ≤ n ≤ 3000
1 ≤ 字符串长度 ≤ 3000,并且只含有大写字母。

题解:
DFS+数学+排序+分治
先找出名字的最大长度,然后每个根据这个补位
然后按字典序排序名字
设f[i]为i个数的排列方案 那么f[i]=i!
递推式:
f[0]:=1
f[i]:=f[i-1]*i
//题目数据大,要注意取模以及开int64 / long long
然后dfs分治
从第dep个字母开始,向后寻找不等于的名字的位置,然后分段处理
//初始dep=1
如果第dep位的字母,第l个到第r个名字相同则分为1组,递归分治
dfs:=dfs*dfs(dep+1,l,r)
直到N个名字的第dep位都分到自己的“段”
//每个dfs初始=1
若每次分了gd段,则dfs=dfs*f[gd]
因为分了gd段,我们就看成gd个整体,即gd个不同的数去处理,因为字母相同,所以可以打乱顺序,则要*排列方案f[gd]

const
    modn=1000000007;
var
    k:array [0..3001] of ansistring;
    f:array [0..3001] of int64;
    i,j,n,m,maxl:longint;


function dfs(dep,l,r:longint):int64;
var
    i,j,gd:longint;
begin
    if l=r then exit(1);
    if dep>maxl
       then exit(f[r-l+1]);
    dfs:=1; j:=l; gd:=1;
    for i:=l to r do
       if k[i][dep]<>k[j][dep]
          then begin
                   inc(gd);
                   dfs:=dfs*dfs(dep+1,j,i-1) mod modn;
                   j:=i;
               end;
    if k[r][dep]=k[j][dep]
       then dfs:=dfs*dfs(dep+1,j,r) mod modn;
    dfs:=dfs*f[gd] mod modn;
    exit(dfs);
end;

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

begin
    assign(input,'ranking.in'); reset(input);
    assign(output,'ranking.out');rewrite(output);
    readln(n);
    f[0]:=1;
    for i:=1 to n do
    begin
        readln(k[i]);
        if length(k[i])>maxl
           then maxl:=length(k[i]);
    end;
    for i:=1 to 3000 do
        f[i]:=(f[i-1]*i) mod modn;
    for i:=1 to n do
      for j:=1 to maxl-length(k[i]) do k[i]:=k[i]+'0';
    qsort(1,n);
    writeln(dfs(1,1,n));
    close(input); close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值