第二场CF,3题收场
http://codeforces.com/contest/611/problem/A
询问2016有多少星期几和几号
打表?
var
n:longint;
a:string;
begin
readln(n,a);
if a=' of week'
then begin
case n of
1:writeln(52);
2:writeln(52);
3:writeln(52);
4:writeln(52);
5:writeln(53);
6:writeln(53);
7:writeln(52);
end;
end
else begin
if n<=29
then writeln(12)
else
if n=30
then writeln(11)
else writeln(7);
end;
end.
http://codeforces.com/contest/611/problem/B
询问[L,R]中转为二进制后只有一个0的数的个数
L,R<=108
所以预处理出所有满足条件的数(不超过2000个)
询问是扫一遍即可
var
x:array[0..2000]of int64;
y:array[0..100]of int64;
sum,i,j,k,ans:longint;
l,r,t:int64;
a,b:int64;
begin
sum:=0; readln(a,b);
y[0]:=1;
for i:=1 to 62 do
y[i]:=y[i-1]*2;
for i:=1 to 61 do
for j:=1 to i-1 do
begin
inc(sum);
x[sum]:=y[i]-1-y[j-1];
end;
ans:=0;
for i:=1 to sum do
if (x[i]>=a)and(x[i]<=b)
then inc(ans);
writeln(ans);
end.
http://codeforces.com/contest/611/problem/C
给定棋盘,若干询问,矩形区域内能放1x2的骨牌方案数
其实不是覆盖问题,就是只放一个有多少位置,要么横着放,要么竖着放,再搞个前缀和就行
var
w,sum1,sum11,sum2,sum22,sum:array[0..505,0..505]of longint;
i,j,k,l:longint;
n,m,t,a,b,c,d,ans:longint;
cha:char;
begin
readln(n,m);
for i:=1 to n do
begin
for j:=1 to m do
begin read(cha); if cha='.' then w[i,j]:=0 else w[i,j]:=1; end;
readln;
end;
for i:=1 to n do
for j:=1 to m do
begin
sum[i,j]:=sum[i-1,j]+sum[i,j-1]-sum[i-1,j-1];
if (w[i,j]=0)and(w[i-1,j]=0)and(i>=2) then inc(sum[i,j]);
if (w[i,j]=0)and(w[i,j-1]=0)and(j>=2) then inc(sum[i,j]);
end;
for i:=1 to n do
for j:=2 to m do
if (w[i,j]=1)or(w[i,j-1]=1)
then sum2[i,j]:=sum2[i,j-1]
else sum2[i,j]:=sum2[i,j-1]+1;
for j:=1 to m do
for i:=2 to n do
if (w[i,j]=1)or(w[i-1,j]=1)
then sum1[i,j]:=sum1[i-1,j]
else sum1[i,j]:=sum1[i-1,j]+1;
readln(t);
for l:=1 to t do
begin
readln(a,b,c,d);
ans:=0;
for i:=a to c do
if (w[i,b]=1)or((w[i,b-1]=1)or(b=1))
then inc(ans,sum2[i,d]-sum2[i,b-1])
else inc(ans,sum2[i,d]-sum2[i,b-1]-1);
for i:=b to d do
if (w[a,i]=1)or((w[a-1,i]=1)or(a=1))
then inc(ans,sum1[c,i]-sum1[a-1,i])
else inc(ans,sum1[c,i]-sum1[a-1,i]-1);
writeln(ans);
end;
end.
http://codeforces.com/contest/611/problem/D
给定一个序列,然后把序列切开,使切出来的数严格递增
DP
dp[i,j]:前i个位置,最后切出来的是后j个
dp[0,j]=1
很明显的 O(N3)的DP
dp[i,j]=dp[i−j,k] (k<j)or((x[i−j−k+1,i−j]<x[i−j+1,i])and(k=j))
我们发现每次是在 dp[i−j,k] 相当于一个前缀和的转移,所以我们维护一个 sum[i,j]
sum[i,j] 负责转移 (k<j) 的部分
当 k>j 时一定不转移
当 k=j 时我们要判断两个等长的字符串的大小,我们用预处理来解决这个问题
lcp:最长公共前缀
我们定义 lcp[i,j]为从i开始和从j开始的最长公共前缀长度
当两部分相等时lcp大于等于他俩的长度
否则我们判断lcp+1位置的字符大小即可
最后, lcp可以用O(N2)的DP处理出来
lcp[i,j]=lcp[i+1,j+1] (x[i]=x[j])
lcp[i,j]=0 (x[i]<>x[j])
当然也可以后缀数组求出,,,
var
lcp,sum,dp:array[0..5005,0..5005]of longint;
i,j,k:longint;
n,ans,tt:longint;
x,a,b:ansistring;
begin
readln(n);
readln(x);
for i:=n downto 1 do
for j:=i downto 1 do
if x[i]=x[j]
then lcp[i,j]:=lcp[i+1,j+1]+1
else lcp[i,j]:=0;
for i:=1 to n do
for j:=1 to n do
begin
if i-j<0 then begin sum[i,j]:=sum[i,j-1]; continue; end;
if i-j=0
then begin dp[i,j]:=1; sum[i,j]:=(sum[i,j-1]+dp[i,j])mod 1000000007; continue; end
else
if x[i-j+1]='0'
then begin dp[i,j]:=0; sum[i,j]:=(sum[i,j-1]+dp[i,j])mod 1000000007; continue; end;
if i-2*j>=0
then begin
tt:=lcp[i-j+1,i-2*j+1];
if (tt>=j)or(x[i-2*j+tt+1]>x[i-j+tt+1])
then dp[i,j]:=(dp[i,j]+sum[i-j,j-1])mod 1000000007
else dp[i,j]:=(dp[i,j]+sum[i-j,j-1]+dp[i-j,j])mod 1000000007;
end
else dp[i,j]:=(dp[i,j]+sum[i-j,i-j])mod 1000000007;
sum[i,j]:=(sum[i,j-1]+dp[i,j])mod 1000000007;
end;
ans:=0;
for i:=1 to n do
ans:=(ans+dp[n,i])mod 1000000007;
writeln(ans);
end.