2017.7.14 C组总结

77 篇文章 0 订阅
37 篇文章 0 订阅

NO.1

题目描述:有n个长方形,给出它的左下角和右上角,求出q个点被多少个长方形覆盖(平行于x轴和y轴)

思路:差分+前缀和
这里写图片描述

代码:

var n,i,x,y,x1,y1,j,q:longint;
    a,f:array[0..3001,0..3001]of longint;
begin
  assign(input,'square.in');
  assign(output,'square.out');
  reset(input);
  rewrite(output);
  readln(n);
  for i:=1 to n do
    begin
      readln(x,y,x1,y1);
      inc(a[x,y]);
      inc(a[x1+1,y1+1]);
      dec(a[x1+1,y]);
      dec(a[x,y1+1]);
    end;
  for i:=1 to 3000 do
    for j:=1 to 3000 do
      f[i,j]:=f[i-1,j]+f[i,j-1]-f[i-1,j-1]+a[i,j];
  readln(q);
  for i:=1 to q do
    begin
      readln(x,y);
      writeln(f[x,y]);
    end;
  close(input);
  close(output); 
end.

NO.2

题目描述:给出N个无序不重复的数,再有M个询问,每次询问一个数是否在那N个数中,若在,则ans增加2^K,K为该数在原数列中的位置。由于ans过大,所以只要求你输出ans mod 10^9+7。

思路:快速幂+排序+二分
先做一遍快排,为二分作准备
那么每次读入一个数,就可以二分求出位置
求2^k次方做一遍快速幂(dalao都说可以不用跑快速幂,在前面预处理就好了)

代码:

#include<cstdio>
#include<iostream>
using namespace std;
long long ans,a[100000],x;
int n,m,l,c[100000];
void qsort(int l,int r)
{
    if (l>=r) return;
    int i=l,j=r;
    long long mid=a[(l+r)/2];
    do
    {
        while (a[i]<mid)i++;
        while (a[j]>mid)j--;
        if (i<=j)
        {
            a[0]=a[i];
            a[i]=a[j];
            a[j]=a[0];
            c[0]=c[i];
            c[i]=c[j];
            c[j]=c[0];
            i++; j--;
        }
    }
    while(i<=j);
    qsort(l,j);
    qsort(i,r);
}
int find(long long x)
{
    int l=1,r=n;
    while (l<=r)
    {
        int mid=(l+r)/2;
        if (a[mid]==x) return mid;
        if (a[mid]>x) r=mid-1;
        else l=mid+1;
    }
    return -1;
}
long long ksm(int b)
{
    long long r=1,base=2;
    while(b)
    {
        if(b&1) r=(r*base)%1000000007;
        base=(base*base)%1000000007;
        b>>=1;
    }
    return r;
}
int main()
{
    freopen("sfxx.in","r",stdin);
    freopen("sfxx.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        c[i]=i;
    }
    qsort(1,n);
    for (int i=1;i<=m;i++)  
    {
        scanf("%lld",&x);
        l=find(x);
        if (l!=-1) ans=(ans%1000000007+ksm(c[l])%1000000007)%1000000007;
    }
    printf("%lld",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

NO.3

题目描述:有l回合操作,其中第一回合是给出的,如果上一轮第i列放了棋子,那么这一轮第i列就不能放棋子,但再下一轮可以放,求在满足情况下有多少种放棋子的方案

思路:状压dp
设f[i][j]为第i轮状态为j的方案数
状态转移方程就是f[i][j]=f[i][j]+f[i-1][k](k表示为上个回合可以转换到j的数)
如果要k满足它可以转换到j,就满足j and k=0(因为and 只有在同一个位上有两个1才肯能有值,题目要没有相邻的棋子,如果=1就满足了)
那么问题就变成了求j和k的问题
首先,将第1轮的所有状态(1 shl n-1)设为1
然后,枚举i(2<=i<=l),枚举j可能到达的状态(1<=j<=1 shl n-1)
那么就只剩下k了,如果像j那样枚举,只能拿到90%
100%怎么做呢?
就用while做, 有两种情况:
①j and k=0,那么转移,k++
②就是100%的优化,如果j and k<>0 就表示这两轮在有相邻的棋子,那么k=k+j and k就把有相邻的棋子弄没了
最后的答案就是将读入的数压成十进制x,输出f[l][x]

代码:

var  n,l,i,j:longint;
     ans,k,y,x:int64;
     a:array[0..50]of longint;
     f:array[0..50,0..50000]of int64;
begin
  assign(input,'old.in');
  assign(output,'old.out');
  reset(input);
  rewrite(output);
  readln(n,l);
  y:=0;
  for i:=1 to n do
    begin
      read(a[i]);
      if i=1 then y:=1 else y:=y*2; 
      if a[i]=1 then x:=x+y;
    end;
  for i:=0 to 1 shl n-1 do f[0,i]:=1;
  for i:=1 to l-1 do
    begin
      for j:=0 to 1 shl n-1 do
        begin
          k:=0;
          while (k<=1 shl n-1) do
            begin
              if (j and k=0) then
                begin
                  f[i,j]:=(f[i,j]+f[i-1,k])mod 100000007;
                  k:=k+1;
                end;
              if (j and k>0) then k:=k+j and k;
            end;
        end;
    end;
  write(f[l-1,x]);
  close(input);
  close(output);
end.

NO.4

题目描述:有n堆,每堆的值不一定相等,现在有两个人,每个人依次取一堆,要剩下m堆,如果最后剩下的值为偶数,则后手赢,不然则先手赢

思路:数学+模拟
其实这题就是判断奇偶性,所以只用记录它的奇数个数l1,和偶数个数l2
还要定义两个变量,l和r表示先手和后手能取多少次
这题就有五种情况:
①双方都不能取(n=m),直接判断奇偶性
②如果后手能拿走所有的奇数,那么后手赢(因为只剩下偶数)
③如果后手能拿走所有的偶数,并且还剩偶数份,那么后手赢(奇数*偶数=偶数)
④如果先手能拿走所有的偶数,并且还剩奇数份,那么先手赢(奇数*奇数=奇数
⑤如果都不存在以上情况,直接判断输出

代码:

var   n,m,x,l,r,l1,l2,i:longint;
      max:int64;
begin
  assign(input,'destroy.in');
  assign(output,'destroy.out');
  reset(input);
  rewrite(output);
  while not eof() do
    begin
      readln(n,m);
      l1:=0; l2:=0;
      for i:=1 to n do
        begin
          read(x);
          if x mod 2=1 then inc(l1)
                       else inc(l2);
        end;
      readln;
      max:=l1+2*l2;
      if n=m then
        begin
          if max mod 2=1 then writeln('Earth')
                         else writeln('Three-Body');
        end
      else
        begin
          l:=0; r:=0;
          l:=(n-m+1) div 2;
          r:=(n-m) div 2;
          if (r>=l1)or(r>=l2)and((max-l2-l-r) mod 2=0) then writeln('Three-Body')
          else if (l>=l2)and((max-l2-l-r) mod 2=1) then writeln('Earth')
               else if l>r then writeln('Earth')
                           else writeln('Three-Body');
        end;
    end;
  close(input);
  close(output);
end.
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值