夜未央Test1题解

T1 积木游戏             

     树状数组的一个简单应用,建立一个维护左节点的树状数组和一个维护右节点的树状数组,对于add操作,只要在维护左节点的树状数组l处加1,维护右节点的树状数组r处加1,那么询问[l,r]的答案就是左节点数组的r前缀和减去右节点数组的l-1前缀和。

var
  q,suml,sumr,i,j,k,l,r,m,n:longint;
  cl,cr:array[0..60005] of longint;

{file}
procedure openf;
begin
  assign(input,'block.in'); reset(input);
  assign(output,'block.out'); rewrite(output);
end;
procedure closef;
begin
  close(input); close(output);
  halt;
end;

{lowbit}
function lowbit(p:longint):longint;
begin
  exit(p and -p);
end;

{add}
procedure addl(x,num:longint);
begin
  while x<=n+1 do
  begin
    inc(cl[x],num);
    x:=x+lowbit(x);
  end;
end;
procedure addr(x,num:longint);
begin
  while x<=n+1 do
  begin
    inc(cr[x],num);
    x:=x+lowbit(x);
  end;
end;

{get}
procedure getsuml(x:longint);
begin
  suml:=0;
  while x>0 do
  begin
    inc(suml,cl[x]);
    x:=x-lowbit(x);
  end;
end;
procedure getsumr(x:longint);
begin
  sumr:=0;
  while x>0 do
  begin
    inc(sumr,cr[x]);
    x:=x-lowbit(x);
  end;
end;

begin
  {input}
  openf;
  readln(n,m);
  
  {doit}
  for i:=1 to m do
  begin
    readln(q,l,r);
    if q=1 then begin
      addl(l,1);
      addr(r,1);
    end;
    if q=2 then begin
      getsuml(r);
      getsumr(l-1);
      writeln(suml-sumr);
    end;
  end;
  closef;
end.
View Code 1
#include<iostream>
#include<cstdio>
#define lowbit(x) x&(-x)
using namespace std;

int q,i,j,k,l,r,m,n;
int cl[60005],cr[60005];

//file
void openf()
{
  freopen("block.in","r",stdin);
  freopen("block.out","w",stdout);
}
void closef()
{
  fclose(stdin); fclose(stdout);
}

//INT
int INT() 
{
  int res;
  char ch;
  while (ch = getchar(), !isdigit(ch));
  for (res = ch - '0'; ch = getchar(), isdigit(ch);)
  res = res * 10 + ch - '0';
  return res;
}
 
//add
void addl(int x,int num)
{
  for (; x<=n+1; x+=lowbit(x)) cl[x]+=num;
}
void addr(int x,int num)
{
  for (; x<=n+1; x+=lowbit(x)) cr[x]+=num;
}

//get
int suml(int x)
{
  int sum = 0;
  for (; x; x-=lowbit(x)) sum+=cl[x];
  return sum;
}
int sumr(int x)
{
  int sum = 0;
  for (; x; x-=lowbit(x)) sum+=cr[x];
  return sum;
}

int main()
{
  //input
  openf();
  n = INT();
  m = INT();
  
  //doit
  for (i = 1; i<=m; i++)
  {
    q = INT();
    l = INT();
    r = INT();
    if (q==1) 
    {
      addl(l,1);
      addr(r,1);
    }else printf("%d
",suml(r)-sumr(l-1));
  }
  closef();
  return 0;
}  
View Code 2

T2 数字游戏             

     这道题首先要做到的就是如何确定方程,由于按照顺序输出,所以我们假定x1<=x2<=x3<=……<=xn,那么我们先将a排序,由于x1+x2是最小的,所以就是a1,同理x1+x3第二小,为a2,但是需要注意的是x2+x3不一定是第三小的,有可能x1+xk比其小,所以我们从a3开始,一个一个假定为x2+x3,联立之前的几个方程,解出x1,x2,x3,然后如果是负数或是无解则跳过,若解出,将x1+x2,x1+x3,x2+x3的结果从a数组中去掉,然后剩下最小的就一定是x1+x4,于是解出x4,之后如上去掉x4与之前几个解的和,剩下最小的是x1+x5,依次做下去,如果其中一个解为负,则该情况不成立,直到所有解解出,输出即可。

      用样例小小解释一下,a数组排序后为{1,2,3,4,5,6},则x1+x2=1,x1+x3=2,假设x2+x3=3解得x1=0,所以x2=1,x3=2,去掉1,2,3,集合为{4,5,6},则x4+x1为4,x4=4,去除4,5,6,集合为空,所以解成立,输出0,1,2,4。

var
  bo:boolean;
  q,i,j,k,l,m,n,tot:longint;
  b,a:array[0..5001] of longint;
  x:array[0..100005] of longint;

{file}
procedure openf;
begin
  assign(input,'math.in'); reset(input);
  assign(output,'math.out'); rewrite(output);
end;
procedure closef;
begin
  close(input); close(output);
  halt;
end;

{sort}
procedure qsort(l,r:longint);
var
  i,j,mid,t:longint;
begin
  i:=l; j:=r;
  mid:=b[l+random(r-l+1)];
  repeat
    while b[i]<mid do inc(i);
    while b[j]>mid do dec(j);
    if i<=j then begin
      t:=b[i];
      b[i]:=b[j];
      b[j]:=t;
      inc(i); dec(j);
    end;
  until i>j;
  if i<r then qsort(i,r);
  if l<j then qsort(l,j);
end;

begin
  {input}
  openf;
  readln(n);
  n:=n*(n-1) div 2;
  for i:=1 to n do
  read(b[i]);
  randomize;
  qsort(1,n);

  {doit}
  for i:=3 to n do
  begin
    bo:=true;
    move(b[1],a[1],n*sizeof(b[1]));
    x[1]:=(a[1]+a[2]-a[i]);
    if x[1]<0 then continue;
    if x[1] mod 2=0 then
    begin
      x[1]:=x[1] div 2;
      x[2]:=a[1]-x[1];
      if x[2]<0 then continue;
      x[3]:=a[i]-x[2];
      if x[3]<0 then continue;
    end
    else continue;
    a[1]:=0; a[2]:=0;
    a[i]:=0;
    tot:=3;
    for j:=3 to n do
    if a[j]<>0 then
    begin
      inc(tot);
      x[tot]:=a[j]-x[1];
      if x[tot]<0 then begin
        bo:=false;
        break;
      end;
      k:=1;
      for l:=j to n do
      if a[l]=x[tot]+x[k] then
      begin
        a[l]:=0;
        inc(k);
        if k=tot then break;
      end;
      if k<>tot then bo:=false;
    end;
    if bo then begin
      for l:=1 to tot do
      write(x[l],' ');
      closef;
    end;
  end;

  {closef}
  writeln('No solution');
  closef;
end.
View Code 1
#include <iostream> 

using namespace std;

bool bo;
int q,i,j,k,l,m,n,tot;
int b[5001],a[5001];
int x[100005];

//file
void openf()
{
  freopen("math.in","r",stdin);
  freopen("math.out","w",stdout);
}
void closef()
{
  fclose(stdin); fclose(stdout);
}

//INT
int INT() 
{
  int res;
  char ch;
  while (ch = getchar(), !isdigit(ch));
  for (res = ch - '0'; ch = getchar(), isdigit(ch);)
  res = res * 10 + ch - '0';
  return res;
}

//sort
void qsort(int l,int r)
{
  int i,j,t,mid;
  mid = b[(l+r)>>1];
  i = l; j = r;
  do {
    while (b[i]<mid) i++;
    while (b[j]>mid) j--;
    if (i<=j) 
    { 
      t = b[i];
      b[i] = b[j];
      b[j] = t;
      i++; j--;
    }   
  }
  while (i<=j);
  if (i<r) qsort(i,r);
  if (l<j) qsort(l,j);
}

int main()
{
  int step = 0;
  //input
  openf();
  n = INT();
  n = n * (n - 1) / 2;
  if (n==1) 
  {
    cout<<"No solution";
    closef();
    return(0);
  }
  for (i=1; i<=n; i++)
    b[i] = INT();
  qsort(1,n);
  //doit
  for (i=3; i<=n; i++)
  { 
    bo = true;
    for (j=1; j<=n; j++)
    a[j] = b[j];
    x[1] = (a[1]+a[2]-a[i]);
    if (x[1]<0) continue;
    if (x[1]%2==0) 
    {
      x[1] /= 2;
      x[2] = a[1]-x[1];
      if (x[2]<0) continue;
      x[3] = a[i]-x[2];
      if (x[3]<0) continue;
    }
    else continue;
    a[1] = 0; a[2] = 0;
    a[i] = 0;
    tot = 3;
    for (j=3; j<=n; j++)
    if (a[j]!=0)
    {
      x[++tot] = a[j]-x[1];
      if (x[tot]<0)
      {
        bo = false;
        break;
      }
      k = 1;
      for (l=j; l<=n; l++)
      if (a[l] == x[tot]+x[k])
      {
        a[l] = 0;
        k++;
        if (k == tot) break;
      }
      if (k!=tot) bo = false;
    }
    if (bo)
    {
      for (l=1; l<=tot; l++)
      printf("%d ",x[l]);
      //system("pause");
      closef;
      return 0;
    }
  }
  
  cout<<"No solution";
  closef();
  return 0;
}
  
View Code 2

T3 造梦                     

     分析题目,可得一个很显然的贪心,所有石柱相差不超过5那么全部一样是最优的,因为最终高度取决于最短的石柱,然后如何去求这个统一高度呢?我们可以这样设想,如果知道了这个需要的高度,那么验证是否可以达到是不是就非常简单了,只要将每一块石料除以已知高度加到ans上,看是否达到需要的块数就可以了。那么,二分检索的模型就浮出水面了,我们取最长石料为右端点,1为左端点,每一次取中点检验是否可行,然后根据单调性更新左右端点,就可以简单地完成这道题了。

var
  l,r,ans,mid,cnt,i,j,k,m,n:longint;
  a:array[0..1000005] of longint;

{file}
procedure openf;
begin
  assign(input,'build.in'); reset(input);
  assign(output,'build.out'); rewrite(output);
end;
procedure closef;
begin
  close(input); close(output);
  halt;
end;

{check}
function check(x:longint):boolean;
begin
  cnt:=0;
  for i:=1 to m do
  begin
    inc(cnt,a[i] div x);
    if cnt>=n then exit(true);
  end;
  exit(false);
end;

begin
  {input}
  openf;
  readln(m,n);

  {doit}
  for i:=1 to m do
  begin
    read(a[i]);
    if a[i]>r then r:=a[i];
  end;
  l:=1;
  repeat
    mid:=(l+r)>>1;
    if check(mid) then begin
      ans:=mid;
      l:=mid+1;
    end
    else r:=mid-1;
  until l>r;

  {output}
  writeln(ans);
  closef;
end.
View Code 1
#include <iostream> 
#include <cstdio>

using namespace std;

const int size=1000005;

int l,r,ans,mid,cnt,i,j,k,m,n;
int a[size];

//file
void openf()
{
  freopen("build.in", "r", stdin);
    freopen("build.out", "w", stdout);
}
void closef()
{
  fclose(stdin); fclose(stdout);
}

//check
bool check(int x)
{
  cnt = 0;
  for (i = 0; i < m; i++)
  {
    cnt+=(a[i] / x);
    if (cnt >= n) return(true);
  }
  return(false);
}

//INT
int INT() 
{
  int res;
  char ch;
  while (ch = getchar(), !isdigit(ch));
  for (res = ch - '0'; ch = getchar(), isdigit(ch);)
  res = res * 10 + ch - '0';
  return res;
}

//main
int main()
{  
  openf();
  m = INT();
  n = INT();
  
  for (i = 0; i < m; i++)
  {
    a[i] = INT();
    if (a[i] > r) r = a[i];
  }
  l = 1;
  do
  {
    mid = (l + r) >> 1;
    if (check(mid)) 
    {
      ans = mid;
      l = mid + 1;
    }
    else r = mid-1;
  }
  while (l <= r);
  
  printf("%d",ans);
  //system("pause");
  closef;
  return(0);
}
  
View Code 2

夜未央Test1标程 

愿你出走半生,归来仍是少年

原文地址:https://www.cnblogs.com/forever97/p/3438137.html