NOIP2016模拟赛 day6

转移阵地,我们继续(伤害)

---------------------------------------------(我是分割线)-------------------------------------------------

T1:city

  题目:给定一棵最小生成树(保证唯一),求其完全图的最小边权和

  数据规模:对于100%的数据,点数n≤20000

  思路:手动模拟一次,发现对于最后完全图的边分为两种情况,一种是于最小生成树内的,另外一种是新加的。对于所有新加的边,为了保证最小生成树的唯一,设从i->j经过的边长分别为a,b,c,那么显然可得,i到j之间的最小边长min(i,j)=max{a,b,c}+1,考虑最小生成树内的每一条边都会有对答案的贡献,那么将每一条边按权值由大到小排序后,每一条边对于答案的贡献即为(w+1)*siz[fx]*siz[fy]-1,之后将这条边擦去。以上操作均可以用并查集完成。

代码:

<span style="font-size:24px;">const
  maxn=40000;
type
  rec=record
        x,y,w:longint;
      end;
var
  a:array[1..maxn] of rec;
  f,siz:array[1..maxn] of longint;
  i,n,fx,fy:longint;
  ans:int64;
  procedure qsort(l,r:longint);
  var
    i,j:longint;
    mid,tmp:rec;
  begin
    i:=l; j:=r; mid:=a[(l+r) div 2];
    repeat
      while a[i].w<mid.w do inc(i);
      while a[j].w>mid.w 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;
  function find(x:longint):longint;
  begin
    if f[x]=x then exit(f[x]);
    f[x]:=find(f[x]);
    exit(f[x]);
  end;
begin
  readln(n);
  for i:=1 to n-1 do readln(a[i].x,a[i].y,a[i].w);
  qsort(1,n-1);
  for i:=1 to n do f[i]:=i;
  for i:=1 to n do siz[i]:=1;
  for i:=1 to n-1 do
  begin
    fx:=find(a[i].x);
    fy:=find(a[i].y);
    ans:=ans+(a[i].w+1)*siz[fx]*siz[fy]-1;
    f[fx]:=fy;
    siz[fy]:=siz[fy]+siz[fx];
  end;
  writeln(ans);
end.
</span>

T2:coin

  题目:给定n种硬币,每种硬币的面值为ai,各有无限个。有T个询问,询问能否用给出的n种硬币凑出面值M,输出最小的硬币个数,否则输出-1

  数据规模:对于100%的数据,n≤100,1≤ai≤10^3,1≤M≤10^16,1≤T≤10^5

  思路:乍一看,怎么感觉都是背包问题,但是一看数据规模就知道没那么简单。换个角度思考,当M在没有到达一个一定的范围之前,无脑拿最大的肯定是最优的,那在进入到一定范围之后,选取最优值即可,而明显那一部分是我们可以预处理的,鉴于M的范围,选择背包预处理1~max(ai)^2,那么ans[M]=op+f[M-op*mx],op=(M-max(ai)^2-1) div mx+1

代码:

<span style="font-size:24px;">const
  inf=1684300900;
var
  a:array[1..2000] of longint;
  f:array[1..2000000] of longint;
  n,i,j,t:longint;
  m,ans,mx,op,tmp:int64;
  function max(a,b:longint):longint;
  begin
    if a>b then exit(a) else exit(b);
  end;
  function min(a,b:longint):longint;
  begin
    if a<b then exit(a) else exit(b);
  end;
begin
  readln(n);
  for i:=1 to n do read(a[i]);
  mx:=0;
  for i:=1 to n do mx:=max(mx,a[i]);
  fillchar(f,sizeof(f),100);
  for i:=1 to n do f[a[i]]:=1;
  for i:=1 to sqr(mx) do
    for j:=1 to n do f[i+a[j]]:=min(f[i+a[j]],f[i]+1);
  readln(t);
  while t>0 do
  begin
    readln(m);
    dec(t);
    if (m<=sqr(mx)) then
    begin
      if f[m]<>inf then writeln(f[m]) else writeln(-1);
      continue;
    end;
    op:=(m-sqr(mx)-1) div mx+1;
    if f[m-op*mx]=inf then writeln(-1) else writeln(f[m-op*mx]+op);
  end;
end.

</span>

T3:shugo(日常惨案,全部人都报0_(:зゝ∠)_,等后续施工吧)

  题目:SingleLyra是一名考古学家,有一天他发现了远古时期的一段文本,这段文本是一个严格上升的十进制数序列,然而现在所有数字连在了一起,因此SingleLyra只能得到一个十进制字符串。        SingleLyra想恢复出来这段文字,为了更加符合原始文本,需要将序列的最后一个数字尽可能小,在此基础上,要求这个序列的字典序尽量大。(字典序尽量大指的是,如果有相同的方案,要求第一个数最大,若第一个数相同,要求第二个数最大,以此类推)。  注意:恢复时允许前导0的出现。

  数据规模:对于 30% 的数据:1<=|S|<=2000; 对于 100% 的数据:1<=|S|<=100000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值