jzoj 模拟赛总结(2017.07.08)

来源:互联网 发布:北京房价数据库 编辑:程序博客网 时间:2024/06/05 15:50

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.