jzoj 模拟赛总结(2017.07.09)

T1:
最大值:
题目大意:
给定n个数,{x1,x2,…,xn}要求从中选出至少一个数,至多n个数,使得乘积之和最大。

对于70%的数据:1 ≤ n ≤ 9
对于100%的数据: 1 ≤ n ≤ 18,-10 ≤xi≤ 10
-10 ≤xi≤ 10

题解:
DP:
虽然貌似排序,搜索那些这数据都能过,不过还是打了个DP.
f[i,1]表示到第i位最小的负数。
f[i,2]表示到第i位的最大乘积。
然后每次对于f[i,1]就判断前一位是否为正数,如果为则重新记录f[i,1]=1,否则f[i,1]=f[i-1,1],即f[i,1]=min(1,f[i-1,1])
对于f[i,2]就判断前面的数是否为负数,如果是就另外寻解
即f[i,2]=max(1,f[i-1,2]) 然后再判断
f[i,1]=min(f[i-1,2]*x,f[i,1]*x,f[i,1])
f[i,2]在此基础上记录max
f[i,2]=max(f[i-1,1]*x,f[i,2]*x,f[i,2])
时间复杂度:O(2N)

var
    f:Array [0..19,1..2] of int64;
    i,j,n:longint;
    p,q,x:int64;

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

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

begin
    assign(input,'max.in'); reset(input);
    assign(output,'max.out');rewrite(output);
    readln(n);
    readln(f[1,1]);
    f[1,2]:=f[1,1];
    for i:=2 to n do
    begin
         readln(x);
         p:=min(1,f[i-1,1]);
         q:=max(1,f[i-1,2]);
         f[i,1]:=min(q*x,min(p,p*x));
         f[i,2]:=max(p*x,max(q,q*x));
    end;
    writeln(f[n,2]);
    close(input); close(output);
end.

T2:
火柴:
题目大意:
这里写图片描述
给定一个N位的数(可能会有前导0),将火柴棍重新排列后,能得到的最大的数是多少?

注意不能多出或者少一位, 火柴棍要全部用上.
对于20%的数据:1 ≤ n ≤ 10
对于60%的数据:1 ≤ n ≤ 1000
对于100%的数据: 1 ≤ n ≤ 100000,1 ≤T≤ 10

题解:
贪心:
一开始预处理出0~9要多少个火柴拼,然后枚举出每个数的火柴使用总数.从高到低位,从大到小放数字,注意火柴棒要恰好用完,且位数一样,所以要时刻检查当前火柴棒是否存在符合要求的放置方法,即检查全部放1或者全部放8行不行,因为2占用的火柴少,8占用的火柴多。

const
    num:array [0..9] of longint=(6,2,5,5,4,5,6,3,7,6);
var
    sum,i,j,k,x,t:longint;
    s:ansistring;
    d:string;
begin
    assign(input,'match.in'); reset(input);
    assign(output,'match.out');rewrite(output);
    readln(t);
    for i:=1 to t do
    begin
         readln(s);
         j:=pos(' ',s);
         d:=copy(s,1,j-1);
         val(d,x);
         delete(s,1,j);
         sum:=0;
         for j:=1 to length(s) do sum:=sum+num[ord(s[j])-48];
         j:=x;
         while j>0 do
         begin
              k:=9;
              while ((sum-num[k]>7*(j-1)) or (sum-num[k]<2*(j-1))) do dec(k);
              write(k);
              sum:=sum-num[k];
              dec(j);
         end;
         writeln;
    end;
    close(input); close(output);
end.

T3:
游戏:
在1~N内猜随便一个数M,问最少要问几个问题才能猜出,对于每个问题,即M能否被I整除。

对于20%的数据:1 ≤ n ≤ 20
对于60%的数据:1 ≤ n ≤ 1000
对于100%的数据: 1 ≤ n ≤ 100000

题解:
找规律可以发现:
因为是一次性提出m个问题,然后得到回答,所以可以通过问题反向退出最终可能的所有情况。
然后我们发现最坏情况其实就是为所有质数的幂次<=n的个数
然后用塞质数的方法去塞,然后再去暴力枚举一下,注意去重

var
   f,p:Array [0..100001] of boolean;
   i,n,ans:longint;
   j:int64;
begin
    assign(input,'game.in'); reset(input);
    assign(output,'game.out');rewrite(output);
    readln(n);
    p[1]:=true;
    for i:=2 to n do
       if not(p[i])
          then begin
                   j:=2;
                   while i*j<=n do
                   begin
                         p[i*j]:=true;
                         inc(j);
                   end;
                   j:=i;
                   while j<=n do
                   begin
                        if not(f[j]) then
                        begin
                             f[j]:=true;
                             inc(ans);
                        end;
                        j:=j*i;
                   end;
               end;
    writeln(ans);
    close(input); close(output);
end.

T4:
Hack比赛:
ZCC做题得到了C分。除去rating最高的选手Memset137。ZCC想要hack他们来使他的得分更高。除了ZCC,房间里有N个选手,他们已经被按照rating从小到大排序了(所以Memset137是排在第N个的选手)。当ZCC成功hack了第i个选手时,他会获得i分的收入。你可以假设ZCC hack技术高超,百发百中,可以hack除了Memset137和自己以外的所有选手,而且在此期间没有其他选手干扰。
ZCC想要知道,存在着多少种不同的选择一些人hack的方案,使得他的得分在L和R(C≤L≤R < C+N)之间。
显然答案会很大,请输出答案对998244353取模后的结果。

对于20%的数据: 1≤N≤50;
对于50%的数据: 1≤N≤2000;
对于100%的数据: 1≤N≤100000, 1≤C≤1000000,C≤L≤R < C+N;

题解:
DP:
50分:
不难发现,可以把题目概括为从1到n-1选若干个数和在L-C到R-C的方案数,即
设f[i,j]表前i个使得和为j的方案总数,然后推出O(NC)的做法
f[i,j]=f[i-1,j-i]+F[i-1,j]
时间复杂度:O(NC)

AC做法:
然后我们发现数据R < C+N,说明R-C < N,而前m个数的和为m(m-1)/2。
令m(m-1)/2=n,则m只有根号n级别,那么说明最多选根号2n个数
这时我们可以让f[i,j] 表示选了i个数且其中包含i使得和为j的方案总数,即推出
f[i,j]=f[i,j-i]+f[i-1,j-i]

状态1:有i个数,他们的和是j-i,然后因为每个数都不同,所以各自加上1,构成第二种可能。
状态2:有i-1个数,他们的和是j-i,给他们加上一个i,使得有i个数,和为j

这样可以使得空间时间复杂度大大降低
时间复杂度:O(N*sqrt(2n))

const
    modn=998244353;
var
    f:Array [0.. 448,0..100001] of longint;
    i,j,n,c,l,r:longint;
    ans:longint;
begin
    assign(input,'hack.in'); reset(input);
    assign(output,'hack.out');rewrite(output);
    readln(n,c,l,r);
    l:=l-c; r:=r-c;
    f[0,0]:=1;
    for i:=1 to trunc(sqrt(n))+131 do
    begin
        for j:=i to r do
          f[i,j]:=(f[i,j-i]+f[i-1,j-i]) mod modn;
        for j:=l to r do
          ans:=(ans+f[i,j]) mod modn;
    end;
    writeln(ans);
    close(input); close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值