比赛链接
http://codeforces.com/contest/961
A. Tetris
题目大意
长度为的序列,操作为:在位置+1,如果序列的每个位置都有值,那么每个位置减一同时答案加1,重复操作指导有一个位置为0为止。重复次。
思路
这道题……按题目过程模拟。。
也可以将所有的操作都处理完后一次回答询问。。
话说我当时用的居然是算法?
代码
#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
题目大意
两个长度为的序列,为0或1,可以使用一次操作使得全部变成1,求的最大值。
思路
一开始把所有的的全都设成0并加入答案,然后枚举,计算,用前缀和优化,。
代码
#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个矩形,大小都为,为奇数。问如何将它们拼成1个黑白交替的棋盘,使得将黑染成白,白染成黑的合计次数最小。
不允许翻转和旋转。
思路
显然一个黑白交替的棋盘它的4个部分也是黑白交替的,这样我们只需要枚举每个部分的左上角是黑还是白。
预处理出每个部分左上角变成黑色和白色需要染色的次数。完整的棋盘,每个部分左上角的颜色,一定是两个黑两个白。压缩状态并枚举状态即可。
代码
#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
题目大意
给你个点,问这个点是否能被两条直线覆盖。
思路
这道题是我赛后hack时研究别人代码做出来的。
我只想说:http://codeforces.com/contest/961/submission/36955388
这人的代码简直……暴力枚举经过两点之间的直线,卡时搜索尽可能多次这样的点……
然而这样的算法每次期望的概率将一个YES的数据判断成NO……
经过计算可知:要想出随机数据hack这个人,期望十亿年都不够(然而那时候人类已经灭亡了)
吐槽完毕,进入正题
如果存在一组解,必定有三个点中两个点在同一条直线上。
很容易判断两个给定点在同一直线上的情况下是否有解。
代码
#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没有看,不过估计不可写,有时间再去写吧。