noip模拟赛day8

T1:fibonacci

  题目:大M最近迷上了fibonacci数列,他定义了一种数列叫fibonacccccci数列:                          1、这个数列包含至少2个元素; 2、f[0]和f[1]是任意选取的;3、               f[n+2]=f[n+1]+f[n](n>=0)现在给出一个数列a[1..n],你可以改变数列元素的顺序,使得a[1..m]满足fibonacccccci数列的条件,请求出最大的m。

  数据范围:对于100%的数据,n≤500

  思路:如果只有正数的话会简单很多,因为fibonacci是一个递增的数列,但是实际上题目里面会有负数,但其实也没有什么大不了,直接暴力找也是一样的,最多也就一个数找2n次。

代码:

const
  maxn=500;
var
  n,i,j,ans:longint;
  a:array[1..maxn] of longint;
  vis:array[1..maxn] of boolean;
  function max(a,b:longint):longint;
  begin
    if a>b then exit(a) else exit(b);
  end;
  function match(l,r:longint):longint;
  var
    k,ans:longint;
    flag:boolean;
  begin
    ans:=2;
    fillchar(vis,sizeof(vis),true);
    vis[l]:=false; vis[r]:=false;
    while 1=1 do
    begin
      flag:=false;
      for k:=1 to n do
        if (vis[k]) and (a[l]+a[r]=a[k]) then
        begin
          inc(ans);
          vis[k]:=false;
          l:=r; r:=k;
          flag:=true;
        end;
      if flag=false then break;
    end;
    exit(ans);
  end;
begin
  readln(n); ans:=0;
  for i:=1 to n do read(a[i]);
  for i:=1 to n-1 do
    for j:=i+1 to n do
      ans:=max(ans,match(i,j));
  writeln(ans);
end.

T2:credit

  题目:大M所在的怪兽大学共有n项课程,每项课程都有一个学分vi,各门课程的学分都有同一个上限maxv。在每年的毕业成绩结算中,一个怪兽的总成绩total=minx*cmin+cntmax*cmax,其中minx为这个怪兽n门课程中学分最低课程的分数,cntmax为这个怪兽n门课程中学分达到上限maxv的个数,cmin和cmax为给出的常数。现在学期已经过去大半,大M想要提高自己今年的总成绩。距离学期结束还有m天,每一天大M可以进修任意一门学分未满的课程并使该门课程的学分+1(你也可以什么课都不上)。请你帮帮大M,告诉他他最高可以获得的总成绩为多少。

  数据规模:对于100%的数据,n<=100000,maxv<=10^5,cmax,cmin<=1000,m<=10^15,vi<=maxv;

  思路:首先将学分按从大到小排序,再O(n)枚举要将学分修满的课程数,然后二分套二分找出当前状态学分最小值的最大值,更新答案。

代码:

const
  maxn=100100;
var
  maxv,cmax,cmin,m,ans,now,time,tmp:int64;
  a,s,up,low:array[0..maxn] of int64;
  n,i,x:longint;
  function find(k,d:int64):longint;
  var
    l,r,mid:longint;
  begin
    l:=1; r:=k;
    while l<=r do
    begin
      mid:=(l+r) div 2;
      if low[mid]>d then r:=mid-1 else l:=mid+1;
    end;
    exit(r);
  end;
  procedure qsort(l,r:longint);
  var
    i,j,mid,tmp:longint;
  begin
    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
        tmp:=a[i];
        a[i]:=a[j];
        a[j]:=tmp;
        inc(i);
        dec(j);
      end;
    until i>j;
    if i<r then qsort(i,r);
    if l<j then qsort(l,j); 
  end;
begin
  readln(n,maxv,cmax,cmin,m);
  for i:=1 to n do read(a[i]);
  qsort(1,n);
  s[0]:=0;
  for i:=1 to n do s[i]:=s[i-1]+a[i];
  for i:=1 to n do low[i]:=a[i]*i-s[i];
  for i:=n downto 1 do up[i]:=maxv*(n-i+1)-(s[n]-s[i-1]);
  for i:=n+1 downto 1 do
  begin
    time:=m-up[i];
    if time<0 then break;
    if i=1 then begin now:=n*cmax+maxv*cmin; continue; end;
    x:=find(i-1,time);
    time:=time-low[x];
    tmp:=a[x]+time div x;
    if tmp>=maxv then now:=n*cmax+maxv*cmin else now:=(n-i+1)*cmax+tmp*cmin;
    if now>ans then ans:=now;
  end;
  writeln(ans);
end.

T3:chocolate

  题目:众所周知,Alice和Bob是一对宿敌,每次博弈游戏中Alice和Bob总绞尽脑汁企图战胜对手,而且很不公平地,每次都是Alice先手。然而他们都看上了大M种的一棵巧克力树,为了取得更多的巧克力,他们决定联手合作。大M的巧克力树由n个节点组成,每个节点i上都有v[i]个巧克力,巧克力树上有n-1条树枝连接这整棵树(保证任意两点间存在一条路径)。Alice和Bob一开始都可以选择一个出发起点并获得该节点上的所有巧克力,当然,Alice先选。接下来,Alice和Bob会依次移动到自己节点相邻的节点上,并获得该节点上的所有巧克力。但是,任意节点都只能被经过一次,若Alice走过某个节点x,则Bob不能到达这个节点,当然Alice也不能走回x。问他们总共最多能获得多少巧克力?

  数据规模:对于100%的数据,n<=100000,1<=v[i]<=10^9

  思路:提供其中一种树形DP的解法:首先对于所有加入的边{x,y}加入其反向边{y,x}。用f[i]表示经过第i条边的最优答案,g[i]表示经过第i条边之后子树的最长链。假设第i条边为{y,x},我们用所有的以x为起点的边的(除去{x,y})的g值来更新f[i]。

代码:

const
  maxn=100100;
type
  edge=record
         v,next:longint;
       end;
var
  e:array[0..maxn*2] of edge;
  vis:array[0..maxn*2] of boolean;
  f:array[0..2*maxn,0..1] of int64;
  v:array[0..maxn] of int64;
  head:array[0..maxn] of longint;
  n,tot,i,x,y:longint;
  ans:int64;
  function max(a,b:int64):int64;
  begin
    if a>b then exit(a) else exit(b);
  end;
  procedure add(x,y:longint);
  begin
    inc(tot);
    e[tot].v:=y; e[tot].next:=head[x]; head[x]:=tot;
    inc(tot);
    e[tot].v:=x; e[tot].next:=head[y]; head[y]:=tot;
  end;
  procedure dfs(x:longint);
  var
    tmp,go:longint;
    max1,max2:int64;
  begin
    if vis[x] then exit;
    vis[x]:=true;
    go:=e[x].v; tmp:=head[go]; max1:=0; max2:=0; f[x,0]:=0;
    while tmp<>-1 do
    begin
      if tmp<>x xor 1 then
      begin
        dfs(tmp);
        f[x,0]:=max(f[x,0],f[tmp,0]);
        if f[tmp,1]>max1 then
        begin
          max2:=max1; max1:=f[tmp,1];
        end
        else if f[tmp,1]>max2 then max2:=f[tmp,1];
      end;
      tmp:=e[tmp].next;
    end;
    f[x,0]:=max(f[x,0],max1+max2+v[go]);
    f[x,1]:=max1+v[go];
  end;
begin
  readln(n);
  for i:=1 to n do read(v[i]);
  fillchar(head,sizeof(head),255); tot:=-1;
  for i:=1 to n-1 do
  begin
    readln(x,y);
    add(x,y);
  end;
  ans:=0;
  for i:=0 to n-2 do
  begin
    dfs(2*i);
    dfs(2*i+1);
    ans:=max(ans,f[2*i,0]+f[2*i+1,0]);
  end;
  writeln(ans);
end.


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值