您的位置:首页 > 编程语言 > Go语言

Codeforces Good Bye 2015 (A,B,C,D)

2016-01-19 08:43 387 查看

Codeforces Good Bye 2015 (A,B,C,D)

tags : Codeforces

Codeforces Good Bye 2015 ABCD
A New Year and Days 水题
题意

解析

代码

BNew Year and Old Property 二进制
题意

解析

代码

CNew Year and Domino 前缀和
题意

解析

代码

DNew Year and Ancient Prophecy DP
题意

解析

代码

竟然有转载还不附上原地址的为了测试对方是不是爬虫我决定加上这一段
原文在CSDN上链接httpblogcsdnnetlinciferarticledetails50540238

还有我那连域名都还没有的博客上也有一份链接http115282401338080archives90

A. New Year and Days (水题)

题意

如果输入是”k of week”(1<=k<=7),输出2016年有多少个星期k.

如果输入是”k of month”(1<=k<=31),输出2016年有多少个k号.

题目中有提示,2016年1月1日是周五。

解析

水题,随便数。

代码

/**
* Created by 0xLLLLH on 2016/1/18.
* Email : linlihao159@gmail.com
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;

#define MAXN    100000+100

int main()
{
int k;
char type[100];
scanf("%d of %s",&k,type);
int ans = 0;
if (strcmp(type, "week") == 0)
{
ans=366 / 7;
if (k < 5 && 5 + 366 % 7 - 7 >= k)
ans++;
else if (k >= 5 && 5 + 366 % 7 > k)
ans++;
}
else if (strcmp(type, "month") == 0)
{
if (k <= 29)
ans = 12;
else if (k == 30)
ans = 11;
else if (k == 31)
ans = 7;
}
printf("%d\n",ans);
return 0;
}


B.New Year and Old Property (二进制)

题意

输出[l,r]范围内满足
二进制表示中只有一个0
的数字的个数。

解析

简单二进制运算,通过
(1LL << i)-1
得到二进制为i个1的数,然后尝试减去
(1LL<<j)
,得到满足
二进制表示中只有一个0
的数,统计在[l,r]范围内的数目即可。

代码

/**
* Created by 0xLLLLH on 2016/1/18.
* Email : linlihao159@gmail.com
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;

#define MAXN    100000+100

int main()
{
long long l, r;
scanf("%lld%lld",&l,&r);
int cnt = 0;
for (int i = 2; i < 63; i++)
{
for (int j = 0; j < i-1; j++)
{
long long x = (1LL << i) - 1 - (1LL << j);
if (x >= l&&x <= r)
cnt++;
}
}
printf("%d\n",cnt);
return 0;
}


C.New Year and Domino (前缀和)

我写的时候脑抽了选择使用
全部组合数目-不合法组合数目
的方法来计算,效果与直接算合法数目相同,但完全是多此一举。

题意

给你一个由’.’和’#’组成的地图(h*w),以及q次查询。对于每次查询,输出以(x1,y1)为左上角,以(x2,y2)为右下角的矩形中有多少对相邻的’.’。

解析

主要的想法就是利用前缀和。

对于每一行,统计本行从开始到当前位置为止的行相邻的’.’有多少对(前缀和思想)得到Tij。然后,再次利用前缀和思想,实际存储的是从第一行开始到当前行为止对应位置的Tij的和,记为Rij。

对于每一列,我们可以用同样的方法得到对应的Cij

然后,对于每次询问,我们可以使用(Rx2,y2−Rx1−1,y2)−(Rx2,y1−Rx1−1,y1)计算出询问矩阵中合法的行方向组合的数目。同理,可以得到列方向上的组合的数目,相加即可得到答案.

队友说可以不用求第二次前缀和,而是每次循环的时候去统计,,,我一直以为这样会超时来着。

代码

/**
* Created by 0xLLLLH on 2016/1/18.
* Email : linlihao159@gmail.com
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>

using namespace std;

#define MAXN    100000+100

char mp[555][555];
int row[555][555];
int col[555][555];

int main()
{
int h, w;
scanf("%d%d",&h,&w);
getchar();
for (int i = 1; i <= h; i++)
scanf("%s", &mp[i][1]);
memset(row,0,sizeof (row));
memset(col, 0, sizeof(col));
for (int i = 1; i <= h; i++)
{
//求当前行方向不合法的数目
for (int j = 1; j <= w; j++)
{
if (j == 1)
row[i][j] =  0;
else
{
row[i][j] = row[i][j - 1];
if ((mp[i][j] == '#') || (mp[i][j - 1] == '#')) row[i][j]++;
}
}
//累加成前缀和
if (i != 1)
for (int j = 1; j <= w; j++)
row[i][j] += row[i-1][j];
}
for (int i = 1; i <= w; i++)
{
for (int j = 1; j <= h; j++)
{
//求当前列方向上不合法的数目
if (j == 1)
col[j][i] =  0;
else
{
col[j][i] = col[j - 1][i];
if ((mp[j][i] == '#') || (mp[j - 1][i] == '#')) col[j][i]++;
}
}
if (i != 1)
for (int j = 1; j <= h; j++)
col[j][i] += col[j][i-1];
}
int q;
scanf("%d", &q);
while (q--)
{
long long x1, y1, x2, y2;
scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
long long s = (x2 - x1)*(y2 - y1 + 1) + (x2 - x1 + 1)*(y2 - y1);
long long ban_row = (row[x2][y2] - row[x1-1][y2]) - (row[x2][y1] - row[x1-1][y1]);
long long ban_col = (col[x2][y2] - col[x2][y1-1]) - (col[x1][y2] - col[x1][y1-1]);
printf("%lld\n",s-ban_row-ban_col);
}
return 0;
}


D.New Year and Ancient Prophecy (DP)

这题想了一下午,终于整体的dp想对了,结果一发直接TLE,后来经过队友提醒才知道要预处理公共前缀,而不是像我之前那样傻傻的进行字符串比较。

题意

将一个由数字组成的字符串分为许多段(不改变顺序),要求子串满足:

每一个子串都代表一个正整数

子串代表的数字严格递增

子串没有前导0

求所有满足要求的划分数,输出结果对(109+7)取模的结果。

解析

暴力显然不行,所以我们使用DP来解。

首先,使用
dp[i][j]
储存到第i个字符为止,划分后数字最大长度不超过j的方案数。

然后,对于每个位置i,设第二大数字为num1,起始位置s1,最大的数字为num2,起始位置s2。

然后首先要知道,如果
len(num1) < len(num2)
,则定有
num1 < num2
。除此之外,如果
len(num1)==len(num2)
且num1的字典序比num2小,也有
num1 < num2
。其他情况都是不满足要求的。

对于每个结束位置i,长度j的num2,其方案数
dp[i][j]
应为num1取不同长度的时候的方案数之和,也就是
dp[i-j][j]
或者
dp[i-j][j-1]
。(我们的
dp[i][j]
存的就是累计值)

所以有以下状态转移方程式:

dp[i][j]=∑j=1if(i,j)

其中

f(i,j)={dp[i−j][j]dp[i−j][j−1]if len(num1) = len(num2)if len(num1) ≤ len(num2)

需要注意的是,直接使用strcmp()或类似的方法比较字典序会超时。我们可以先预处理出字符串本身不同位置间的最长公共前缀
comm[x][y]
,然后以O(1)的复杂度比较。

代码

/**
* Created by 0xLLLLH on 2016/1/18.
* Email : linlihao159@gmail.com
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>
#include <vector>

using namespace std;

#define MAXN    5000+100
#define MOD     1000000007

int n;
char str[MAXN];

//储存到第i个字符为止,划分后子串最大长度不超过j的方案数
long long dp[MAXN][MAXN];
long long comm[MAXN][MAXN];

//用于比较字符串
int scmp(int s1, int s2, int len)
{
long long co = comm[s1][s2];
if (co >= len)
return 0;
else
{
if (str[s1 + co] < str[s2 + co])
return -1;
else if (str[s1 + co] > str[s2 + co])
return 1;
}
return 0;
}

//预处理出str中位置i和位置j的前缀长度
void getCommomPrefix()
{
for (int i = 0; i <= n; i++)
{
comm[i]
= 0;
comm
[i] = 0;
}
for (int i = n-1; i >=0; i--)
{
for (int j = n-1; j >=0; j--)
{
comm[i][j] = comm[i + 1][j + 1];
if (str[i] == str[j])
comm[i][j]++;
else
comm[i][j] = 0;
}
}
}

int main()
{
scanf("%d", &n);
getchar();
scanf("%s",str);
getCommomPrefix();
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; i++)
{
if (i == 0)
{
dp[0][0] = 0;
for (int j = 1; j <= n; j++)
dp[0][j] = 1;
}
else
{
for (int len = 1; len<=n; len++)
{
dp[i][len] = dp[i][len - 1];    //dp[i][j]存的是长度0..len的累计值,要先赋初值为长度len-1时的值
int s1 = i - len * 2 + 1;       //第二长的字符串的起始位置(假设其长度为len)
int s2 = i - len + 1;           //最后,也是最长的字符串的起始位置
if (s2 < 0 || str[s2] == '0')
continue;
if (s1 >= 0)    //如果第二长的字符串长度能取到len
{
if (str[s2] != '0')     //字符串不能有前导0
{
if (str[s1] != '0'&&scmp(s1, s2, len) < 0)
dp[i][len] += dp[s2-1][len];
else
dp[i][len] += dp[s2-1][len - 1];
dp[i][len] %= MOD;
}
}
else    //取不到len,则最长为s2-1
{
if (s2 == 0)
dp[i][len]++;
else
dp[i][len] += dp[s2 - 1][len];
dp[i][len] %= MOD;

}
}
}
}
printf("%lld\n",dp[n-1]
);
return 0;
}


竟然有转载还不附上原地址的?为了测试对方是不是爬虫我决定加上这一段

原文在CSDN上,链接http://blog.csdn.net/lincifer/article/details/50540238

还有我那连域名都还没有的博客上也有一份,链接http://115.28.240.133:8080/archives/90

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  codeforces