NOIP试题解析

                                                                 NOIP试题解析           by QTY_YTQ

noip2010关押罪犯(并查集) 

题意是有n个罪犯关在两个监狱中,其中有m对罪犯有仇恨关系,如果有仇恨的罪犯关在一起会产生一定影响力的事件,要求安排罪犯位置使产生影响力最大的事件影响最小。

可以用并查集来做,每个罪犯抽象为两个点,一个表示该罪犯关押在1监狱,另一个表示该罪犯关押在2监狱,我们将罪犯仇恨关系按影响的大小排序,每次选取影响力最大的一对罪犯(x,y),尽可能不让它们在一个监狱内,将x1和y2合并,将x2和y1合并,继续往后做,知道两个罪犯已经在同一集合内,这时不能让这两个罪犯在不同监狱中,则他们之间的影响力就是最小的最大影响力,

代码:

program prison;
var
  a,b,c:array[0..100000]of longint;
  f:array[0..40000]of longint;
  n,i,m,x,y,v,x1,y1,x2,y2:longint;
function find(x:longint):longint;
var i,j,k:longint;
begin
  i:=x; j:=i;
  while f[i]<>i do i:=f[i];
  while j<>i do
  begin k:=f[j];f[j]:=i; j:=k; end;
  exit(i);
end;
procedure qsort(l,r:longint);
var i,j,m,t:longint;
begin
  i:=l; j:=r; m:=c[(l+r) div 2];
  repeat
   while c[i]>m do inc(i);
   while c[j]<m do dec(j);
   if i<=j then
    begin
      t:=c[i]; c[i]:=c[j]; c[j]:=t;
      t:=a[i]; a[i]:=a[j]; a[j]:=t;
      t:=b[i]; b[i]:=b[j]; b[j]:=t;
      inc(i); dec(j);
    end;
  until i>j;
  if j>l then qsort(l,j);
  if i<r then qsort(i,r);
end;
begin
  readln(n,m);
  for i:=1 to n*2 do f[i]:=i;
  for i:=1 to m do
   begin
     readln(x,y,v);
     a[i]:=x; b[i]:=y; c[i]:=v;
   end;
  qsort(1,m);
  for i:=1 to m do
   begin
     x1:=find(a[i]); x2:=find(a[i]+n);
     y1:=find(b[i]); y2:=find(b[i]+n);
     if (x1<>y1)and(x2<>y2) then begin f[x2]:=y1; f[y2]:=x1; end
      else begin writeln(c[i]); n:=-1;break;  end;
   end;
  if n>=0 then writeln(0);
end.
View Code

 

noip2011选择客栈(思考+rmq)

题意是有n个客栈排成一排,每种客栈有一个颜色,共k种颜色,有两个人要住在同种颜色的不同客栈中,并且要到这两个客栈之间(包括这两个客栈)的某家客栈喝咖啡,并且花费不超过p,求满足该条件的住宿方案数。

主要思路是,先rmqst算法得到任意两客栈之间客栈喝咖啡的最小花费,可以看出,如果选择i客栈和j客栈最小花费不超过p,则选择i客栈和j以后客栈的最小花费必然不超过p,选择i之前客栈和j客栈的最小花费也不超过p,那么我们从1做到n,对于客栈i,对应颜色x,先判断它左边最靠近它的颜色是x一个客栈与该客栈之间的最小花费是否超过p,超过则计数器直接加上之前满足某一方案且颜色为x的客栈数,没超过则加上i之前颜色为x的客栈总数,得到答案。

代码:

program hotel;
var
  h:array[0..20,0..200000]of longint;
  a,b:array[0..200000]of longint;
  pl:array[0..20]of longint;
  r,c,u:array[0..50]of longint;
  n,i,m,j,k,p,x,s:longint;
function min(x,y:longint):longint;
begin
   if x<y then min:=x else min:=y;
end;
function find(x,y:longint):longint;
var j:longint;
begin
  j:=trunc(ln(y-x+1)/ln(2));
  exit(min(h[j,x],h[j,y-pl[j]+1]));
end;
begin
 assign(input,'hotel.in');
reset(input);
assign(output,'hotel.out');
rewrite(output);
  readln(n,k,p);
  for i:=1 to n do
   readln(b[i],a[i]);
  pl[0]:=1;
  for i:=1 to trunc(ln(n)/ln(2)) do  pl[i]:=pl[i-1]*2;
  for i:=1 to n do  h[0,i]:=a[i];
  for i:=1 to trunc(ln(n)/ln(2)) do
    for j:=1 to n+1-2*i do
     h[i,j]:=min(h[i-1,j],h[i-1,j+pl[i-1]]);
  for i:=1 to n do
   begin
     x:=b[i];
     if u[x]=0 then begin r[x]:=i;u[x]:=1; end
      else
       begin
         k:=find(r[x],i);
         if k<=p then begin c[x]:=u[x];u[x]:=u[x]+1;s:=s+c[x];r[x]:=i; end
          else begin  s:=s+c[x]; u[x]:=u[x]+1; r[x]:=i; end;
       end;
   end;
 writeln(s);
 close(input); close(output);
end.
View Code

 

noip2011聪明的质监员(二分)

题意什么的按题目来,需要注意的每个区间的值是由该区间上重量超过w矿石个数乘上该区间重量超过w矿石的价值和。

二分答案,然后前缀和得到区间上超过w矿石的价值和与数量和,然后判断比s大还是小,利用w越大,y越小的单调性变换上下界。

代码:

program qc;
var
  w,v,x,y,s1,s2:array[0..200000]of int64;
  n,i,m,l,r,mid:longint; s,x1,x2:int64;
function find(k:longint):int64;
var i,j:longint;  sum:int64;
begin
  sum:=0;
  fillchar(s1,sizeof(s1),0); fillchar(s2,sizeof(s2),0);
  for i:=1 to n do
  begin s1[i]:=s1[i-1]+ord(w[i]>=k)*v[i];s2[i]:=s2[i-1]+ord(w[i]>=k); end;
  for i:=1 to m do
    sum:=sum+(s1[y[i]]-s1[x[i]-1])*(s2[y[i]]-s2[x[i]-1]);
  exit(sum);
end;
begin
  assign(input,'qc.in');
reset(input);
assign(output,'qc.out');
rewrite(output);
  readln(n,m,s);
  for i:=1 to n do
  begin  readln(w[i],v[i]); if w[i]>r then r:=w[i]; end;
  for i:=1 to m do
   readln(x[i],y[i]);l:=1;
  while l<=r do
   begin
     mid:=(l+r) div 2;
     if find(mid)<=s then r:=mid-1 else l:=mid+1;
   end;
  x1:=find(l); x2:=find(r);
  if abs(x1-s)<abs(x2-s) then writeln(abs(x1-s)) else writeln(abs(x2-s));
  close(input); close(output);
end.
View Code

 

noip2012借教室(二分+前缀和)

题意是每天有一些教室可以租借,有m份请求分别要租借一定数量教室一段时间,输出最先无法满足的请求序号。

二分答案,利用前缀和记录不同时间租借教室的个数,判断是否满足。

代码:

program classroom;
var
  f,d,s,t,b:array[0..1000000]of int64;
  n,i,m,k,l,r,mid,ans:longint;  sum:int64;
begin
  assign(input,'classroom.in');
  reset(input);
  assign(output,'classroom.out');
   rewrite(output);
  readln(n,m);
  for i:=1 to n do read(f[i]);
  for i:=1 to m do
   readln(d[i],s[i],t[i]);
  l:=0; r:=m;
  while l<=r do
   begin
     mid:=(l+r) div 2;
     fillchar(b,sizeof(b),0);
     for i:=1 to mid do
       begin
         inc(b[s[i]],d[i]);inc(b[t[i]+1],-d[i]);
       end;
     sum:=0;  k:=0;
     for i:=1 to n do
      begin
        sum:=sum+b[i]; if sum>f[i] then begin k:=1; break; end;
      end;
     if k=1 then r:=mid-1
       else begin ans:=mid; l:=mid+1; end;
   end;
  if ans>=m then writeln(0)
   else begin writeln(-1); writeln(ans+1); end;
  close(input); close(output);
end.
View Code

 

noip2013火柴排队(贪心+逆序对)

题意两组火柴,同组火柴高度不同,分别将两组火柴以两两交换形式得到新序列,使两组火柴同位置火柴高度差绝对值和最小,求最少交换次数。

显然1组第一高的火柴要对应2组第一高的火柴,第二高也对应第二高,以此类推,我们对火柴高度离散化后,以一组火柴为参照,求另一组火柴高度逆序对个数即可。

代码:

program match;
type
  arr=array[0..100002]of longint;
var
  a,b,u,v,bit:arr;
  n,i,m:longint; t:int64;
procedure add(i,x:longint);
begin
  while i<=n do
   begin
     bit[i]:=bit[i]+x; inc(i,i and -i);
   end;
end;
function sum(i:longint):longint;
var s:longint;
begin
  s:=0;
  while i>0 do
   begin
     s:=s+bit[i]; dec(i,i and -i);
   end;
  exit(s);
end;
procedure qsort(l,r:longint; var a,u:arr);
var i,j,t,m:longint;
begin
  i:=l; j:=r; m:=a[(i+j) div 2];
  repeat
    while a[i]<m do inc(i);
    while m<a[j] do dec(j);
    if i<=j then
     begin
       t:=a[i]; a[i]:=a[j]; a[j]:=t;
       t:=u[i]; u[i]:=u[j]; u[j]:=t;
       inc(i);dec(j);
     end;
  until i>j;
  if i<r then qsort(i,r,a,u);
  if l<j then qsort(l,j,a,u);
end;
begin
  assign(input,'match.in');
reset(input);
assign(output,'match.out');
rewrite(output);
  readln(n);
  for i:=1 to n do begin read(a[i]);u[i]:=i; end;  readln;
  for i:=1 to n do begin read(b[i]);v[i]:=i; end;
   qsort(1,n,a,u); qsort(1,n,b,v);
  for i:=1 to n do
   begin
    a[i]:=i; b[v[i]]:=u[i];
   end;
  for i:=n downto 1 do
   begin
     inc(t,sum(b[i]));add(b[i],1);
   end;
  writeln(t mod 99999997);
  close(input); close(output);
end.
View Code

 

noip2014联合权值

题意很简单,不说了(其实是我懒得写)。

记录下每个点与之相邻所有点的权值和,权值平方和,最大值,次大值,所有点最大次大值乘积的最大值就是最大联合权值,权值和平方减权值平方和的和就是联合权值和。

代码:

program link;
var
  a,b,v,max1,max2,sum,num:array[0..200000]of int64;
  n,i:longint;  m,ans:int64;
begin
  assign(input,'link.in');
reset(input);
assign(output,'link.out');
rewrite(output);
  readln(n);
  for i:=1 to n-1 do
   readln(a[i],b[i]);
  for i:=1 to n do read(v[i]);
  for i:=1 to n-1 do
  begin
    if v[b[i]]>max1[a[i]] then begin max2[a[i]]:=max1[a[i]];max1[a[i]]:=v[b[i]]; end
      else if v[b[i]]>max2[a[i]] then max2[a[i]]:=v[b[i]];
    if v[a[i]]>max1[b[i]] then begin max2[b[i]]:=max1[b[i]];max1[b[i]]:=v[a[i]]; end
      else if v[a[i]]>max2[b[i]] then max2[b[i]]:=v[a[i]];
    inc(sum[a[i]],v[b[i]]); inc(num[a[i]],sqr(v[b[i]]));
    inc(sum[b[i]],v[a[i]]); inc(num[b[i]],sqr(v[a[i]]));
  end;
  for i:=1 to n do
   if max1[i]*max2[i]>m then m:=max1[i]*max2[i];
  for i:=1 to n do
  begin
    ans:=(ans+(sqr(sum[i])-num[i]) mod 10007) mod 10007;
  end;
  writeln(m,' ',ans);
  close(input); close(output);
end.
View Code

 

noip2014寻找道路

题意很简单不解释。

看清题意很重要,起点终点是由输入提供的,另外符合要求的点其所有出边指向的点与终点联通,无解输出-1.

做起来很简单,所有边反过来求一边终点到各点最短路径,判断每个点是否符合,然后在符合要求的点中求起点到终点最短路。

代码:

program road;
var
  c,a:array[0..10000,0..2000]of longint;
  q:array[0..500001]of longint;
  dis:array[0..10000]of longint;
  g,r:array[0..10000]of boolean;
  n,i,m,j,t,x,y,z,h,u,v:longint;
function min(x,y:longint):longint;
begin
  if x<y then min:=x else min:=y;
end;
begin
  assign(input,'road.in');
  reset(input);
  assign(output,'road.out');
  rewrite(output);
  readln(n,t);
  for i:=1 to t do
   begin
     readln(x,y);
     a[x,0]:=a[x,0]+1; a[x,a[x,0]]:=y;
     c[y,0]:=c[y,0]+1; c[y,c[y,0]]:=x;
   end; readln(x,y);
  for i:=1 to n do dis[i]:=maxlongint div 3;
  fillchar(g,sizeof(g),false);
  h:=0; t:=1; dis[y]:=0; q[1]:=y; g[y]:=true;
  while h<t do
   begin
     h:=h+1; u:=q[h]; g[u]:=false;
     for i:=1 to c[u,0] do
     begin
       v:=c[u,i];
       if dis[u]+1<dis[v] then
        begin
          dis[v]:=dis[u]+1;
          if g[v]=false then
            begin
              t:=t+1; q[t]:=v; g[v]:=true;
            end;
        end;
     end;
   end;
  for i:=1 to n do
   begin
     r[i]:=true;
    for j:=1 to a[i,0] do
     if dis[a[i,j]]>=maxlongint div 3 then begin r[i]:=false; break; end;
   end;
  for i:=1 to n do dis[i]:=maxlongint div 3;
  fillchar(g,sizeof(g),false);
  h:=0; t:=1; dis[x]:=0; q[1]:=x;  g[x]:=true;
  while h<t do
   begin
     h:=h+1; u:=q[h]; g[u]:=false;
     for i:=1 to a[u,0] do
     begin
       v:=a[u,i];
       if (dis[u]+1<dis[v])and(r[v]=true) then
        begin
          dis[v]:=dis[u]+1;
          if g[v]=false then
            begin
              t:=t+1; q[t]:=v; g[v]:=true;
            end;
        end;
     end;
   end; if (r[x]=false)or(dis[y]>=maxlongint div 3) then writeln(-1) else writeln(dis[y]);
  close(input); close(output);
end.
View Code

 

posted @ 2015-11-01 13:32  QTY_YTQ  阅读(314)  评论(0编辑  收藏  举报