比赛链接

http://codeforces.com/contest/961

A. Tetris

题目大意

长度为n的序列,操作为:在ai位置+1,如果序列的每个位置都有值,那么每个位置减一同时答案加1,重复操作指导有一个位置为0为止。重复m次。

思路

这道题……按题目过程模拟。O(nm)
也可以将所有的操作都处理完后一次回答询问。O(n+m)
话说我当时用的居然是O(nm)算法?

代码

#include <cstdio>

const int maxn=1000;

int a[maxn+10],n,m,ans;

int main()
{
  scanf("%d%d",&n,&m);
  while(m--)
    {
      int pos,flag=1;
      scanf("%d",&pos);
      ++a[pos];
      for(register int i=1; i<=n; ++i)
        {
          if(a[i]==0)
            {
              flag=0;
              break;
            }
        }
      if(flag)
        {
          ++ans;
          for(register int i=1; i<=n; ++i)
            {
              --a[i];
            }
        }
    }
  printf("%d\n",ans);
  return 0;
}

B. Lecture Sleep

题目大意

两个长度为n的序列ai,bibi为0或1,可以使用一次操作使得b[m,m+k1]全部变成1,求i=1naibi的最大值。

思路

一开始把所有的bi=1ai全都设成0并加入答案,然后枚举m,计算a[m,m+k1],用前缀和优化,O(n)

代码

#include <cstdio>
#include <algorithm>

const int maxn=100000;

int n,k,a[maxn+10],t[maxn+10],cnt,sum[maxn+10],ans;

int main()
{
  scanf("%d%d",&n,&k);
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d",&a[i]);
    }
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d",&t[i]);
      if(t[i])
        {
          cnt+=a[i];
          a[i]=0;
        }
    }
  sum[0]=0;
  for(register int i=1; i<=n; ++i)
    {
      sum[i]=sum[i-1]+a[i];
    }
  for(register int i=1; i<=n-k+1; ++i)
    {
      ans=std::max(ans,cnt+sum[i+k-1]-sum[i-1]);
    }
  printf("%d\n",ans);
  return 0;
}

C. Chessboard

题目大意

给你一个黑白组成的4个矩形,大小都为n×nn为奇数。问如何将它们拼成1个黑白交替的棋盘,使得将黑染成白,白染成黑的合计次数最小。
不允许翻转和旋转。

思路

显然一个黑白交替的棋盘它的4个部分也是黑白交替的,这样我们只需要枚举每个部分的左上角是黑还是白。
预处理出每个部分左上角变成黑色和白色需要染色的次数。完整的棋盘,每个部分左上角的颜色,一定是两个黑两个白。压缩状态并枚举状态即可。
O(n)

代码

#include <cstdio>
#include <algorithm>
#include <iostream>

const int maxn=200;
const int inf=0x3f3f3f3f;

int a[4][maxn+10][maxn+10],n,wt[4][2],ans;

inline int cnt_bit(int x)
{
  int res=0;
  while(x)
    {
      res+=x&1;
      x>>=1;
    }
  return res;
}

int main()
{
  scanf("%d",&n);
  char ch=getchar();
  for(register int i=0; i<4; ++i)
    {
      for(register int j=1; j<=n; ++j)
        {
          for(register int k=1; k<=n; ++k)
            {
              while((ch<'0')||(ch>'9'))
                {
                  ch=getchar();
                }
              a[i][j][k]=ch-'0';
              ch=getchar();
            }
        }
    }
  for(register int i=0; i<4; ++i)
    {
      for(register int j=0; j<2; ++j)
        {
          for(register int k=1; k<=n; ++k)
            {
              for(register int l=1; l<=n; ++l)
                {
                  if((k+l+j)%2!=a[i][k][l])
                    {
                      ++wt[i][j];
                    }
                }
            }
        }
    }
  ans=inf;
  for(register int i=0; i<16; ++i)
    {
      if(cnt_bit(i)==2)
        {
          int res=0;
          for(register int j=0; j<4; ++j)
            {
              res+=wt[j][(i>>j)&1];
            }
          ans=std::min(ans,res);
        }
    }
  printf("%d\n",ans);
  return 0;
}

D. Pair Of Lines

题目大意

给你n个点,问这n个点是否能被两条直线覆盖。

思路

这道题是我赛后hack时研究别人代码做出来的。
我只想说:http://codeforces.com/contest/961/submission/36955388
这人的代码简直……暴力枚举经过两点之间的直线,卡时搜索尽可能多次这样的点……
然而这样的算法每次期望12的概率将一个YES的数据判断成NO……
经过计算可知:要想出随机数据hack这个人,期望十亿年都不够(然而那时候人类已经灭亡了)
吐槽完毕,进入正题
如果存在一组解,必定有三个点中两个点在同一条直线上。
很容易判断两个给定点在同一直线上的情况下是否有解。
O(n)

代码

#include <cstdio>
#include <cstring>

const int maxn=100000;

struct point
{
  int x,y;
};

point p[maxn+10];
int n,used[maxn+10];

inline int one_line(int a,int b,int c)
{
  if((a==0)||(b==0)||(c==0))
    {
      return 1;
    }
  return 1ll*(p[c].x-p[a].x)*(p[b].y-p[a].y)==1ll*(p[c].y-p[a].y)*(p[b].x-p[a].x);
}

int check(int a,int b)
{
  memset(used,0,sizeof used);
  used[a]=used[b]=1;
  for(register int i=1; i<=n; ++i)
    {
      if((!used[i])&&(one_line(a,b,i)))
        {
          used[i]=1;
        }
    }
  int u=0,v=0,w=0,flag=1;
  for(register int i=1; i<=n; ++i)
    {
      if(!used[i])
        {
          u=v;
          v=w;
          w=i;
          if(!one_line(u,v,w))
            {
              flag=0;
              break;
            }
        }
    }
  return flag;
}

int main()
{
  scanf("%d",&n);
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d%d",&p[i].x,&p[i].y);
    }
  if(check(1,2)||check(2,3)||check(1,3))
    {
      puts("YES");
    }
  else
    {
      puts("NO");
    }
  return 0;
}

后面的题

E貌似很可写,FG没有看,不过估计不可写,有时间再去写吧。