牛客IOI周赛24-普及组
比赛链接
牛客IOI周赛24-普及组
B.数字串
题目描述
给定两个正整数 \(L, R\) ,还有一个数字串 \(s\) (由 \(0 \sim 9\) 等数字组成)。
问现在数字串里面有多少个不同子段 \(s[l, r]\) 组成的数(把子串这一段提出来形成的数字) \(k\) 满足:
给定的 \(L, R\) 没有前导零。
数字串可能有前导零,并且对于给定的数字串中,选出来的子段可以有前导零。两个子段的左端点的位置或者右端点 的位置不一样即说两个子段不一样。
输入描述:
第一行两个整数: \(L, R\)
第二行一个字符串: \(s\) 代表给定数字串
输出描述:
一行, 一个整数, 表示有多少个子段满足条件。
示例1
输入
2 5
13254
输出
4
示例2
输入
1 100
10015478
输出
16
说明
满足条件的子段分别为:
(以标号 1 为第一个数孕)
\(s[1,1], s[1,2], s[1,3], s[2,4]\),
\(s[2,5], s[3,4], s[3,5], s[4,4]\),
\(s[4,5], s[5,5], s[5,6], s[6,6]\),
\(s[6,7], s[7,7], s[7,8], s[8,8]\)
备注:
测试点编号 | 字符串长度 | L,R范围 |
---|---|---|
1∼3 | <=50 | 1<=L,R<=20 |
4∼5 | <=5**10^(4) | 1<=L,R<=10^(18) |
6∼12 | <=5**10^(5) | 1<=L,R<=10^(25) |
对于 \(100\%\) 的数据均有 \(1<=|S|<=5 * 10^{5}, 1<=L, R<=10^{25}\)
解题思路
双指针
遍历 \(i\),计算 \(i\) 的贡献,即 \(i\) 作为最后一个字符时满足条件的子串的个数,即寻找范围内最前面的 \(r\) 和最后面的 \(l\) 到 \(i\) 的数,\(i\) 的贡献即为 \(l-r+1\),\(i\) 后移,\(r\) 和 \(l\) 只会后移,另外这里需注意,这里由于数据会爆long long
,故需用__int128
处理数据
- 时间复杂度:\(O(n)\)
代码
// Problem: 数字串
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/32701/B
// Memory Limit: 262144 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
template <typename T>
void print(T x) {
if (x < 0) putchar('-'), x = -x;
if (x < 10) putchar(x + 48);
else print(x / 10), putchar(x % 10 + 48);
}
template <typename T>
void print(T x, char t) {
print(x); putchar(t);
}
__int128 L,R,l,r,p[30],t;
string s;
int main()
{
read(L),read(R);
cin>>s;
int n=s.size();
s=' '+s;
p[0]=1;
for(int i=1;i<=29;i++)p[i]=10*p[i-1];
int res=0;
l=r=0;
for(int i=1,j=1,k=1;k<=n;k++)
{
l=l*10+s[k]-'0';
r=r*10+s[k]-'0';
while(j<=k&&l>=L)
{
t=l;
if(k-j<=29)
l=l%p[k-j];
j++;
}
while(i<=j-1&&r>R)
{
if(k-i<=29)
r=r%p[k-i];
i++;
}
if(t>=L&&r<=R)
res+=j-i;
}
cout<<res;
return 0;
}
C.银杏林
题目描述
小 \(Y\) 每天上学和放学都会穿过这片银杏林。
银杏林可以看作一片 \(n * m\) 的矩形区域,上面有一些银杏树 用"T"表示,空地 用"."表示。
其中有一些的小路用字符 "R" 表示。
小 \(Y\) 很憙欢晒太阳,但树会挡住阳光,阳光无法穿过树。
现在需要回答 \(Q\) 个询问,每次都要回答太阳从不同方向昭来时小路上有多少处有阳光。
为了方便描述,这里给出一种描述阳光方向的方法:
对于每个询问给出两个整数 \(x, y\) 表示阳光的方向,阳光方向即依次经过 \((0,0) ,(x, y)\) 的射线的方向。
与 \(x\) 轴正半轴交点坐标为整数或 \(y\) 轴正半轴交点坐标为整数的的若干条平行与给定射线的射线都视作阳光 (同时经过 0,0 也算)。
数据保证: \(x * y>=0 \& \& x, y\) 不同时等于 0 !
同时阳光在题目中并不是从给定矩阵的 \((0,0)\) 点出发的,下面给出一个解释 解释:
\(x>0, y>0\) 的时候表示阳光从 给定矩形的左下方射向 给定矩形的右上方
\(x<0, y<0\) 的时候表示阳光从 给定矩形的右上方 射向 给定矩形的左下方
当注意矩阵一直是处于第一象限的,矩阵的行 ( \(\mathrm{n}\) 那一维代表 \(\mathrm{y}\) ),矩阵上面的 \(\left(x_{i}, y_{i}\right)\) 对应平面直角坐标系上坐标 就是 \(\left(n-x_{i}+1, y_{i}\right)\) 。
例如阳光方向为 : \((2,1)\) ,下面三条蓝色的线都是“阳光”
输入描述:
第一行 给定 \(n, m\) 同题面。
第 \(2 \sim n+1\) 行给定 \(n\) 行长为 \(m\) 的字符串, 其中. 代表空地, \(T\) 代表银杏树, \(R\) 代表小路 第 \(n+2\) 行给定 \(Q\) 表示询问数量。
为了方便描述, 这里给出一种描述阳光方向的方法:
对于每个询问给出两个整数 \(x, y\) 表示阳光的方向, 阳光方向即依次经过 \((0,0),(x, y)\) 的射线 (方向是 \((0,0)\) 指向 \((x, y)\) )。
输出描述:
对于每个询问,请输出一行一个整数即此时被阳光照到的 "R" 字符的数量。
示例1
输入
3 3
TRR
TRT
RTT
3
2 1
0 1
1 0
输出
2
1
1
说明
下图中黄色点表示银杏树,绿色点表示小路,橙色线条代表阳光。
询问一:
询问二:
询问三:
示例2
输入
3 3
TRR
TRT
RRT
3
1 2
1 1
0 1
输出
1
4
4
示例3
输入
3 3
TRR
RRT
RTT
4
-2 -1
-1 -2
-1 -1
-1 0
输出
2
2
5
2
测试点编号 | n,m,Q 范围 | x,y 范围以及特殊性质 |
---|---|---|
1∼3 | 1<=n,m<=10,1<=Q<=30 | 0<=x,y<=10,x,y 不同时为 0 |
4∼6 | 1<=n,m<=100,1<=Q<=30 | x**y>=0,x,y 不同时为 0 |
7∼10 | 1<=n,m<=1000,1<=Q<=30 | x**y>=0,x,y 不同时为 0 |
对于 \(100 \%\) 的数据保证: \(1<=n, m<=1000, x * y>=0\) 并且 \(x, y\) 不同时为 \(0,-1000<=\) \(x, y<=1000,1<=Q<=30\)
解题思路
模拟
分类讨论,
-
横或纵坐标为 \(0\),按行或列扫描,遇到
R
,累加到答案中,遇到T
直接break
-
直线从左下方到右上方,在横纵坐标整点开始往右上方走,遇到
R
,累加到答案中,遇到T
直接break
-
直线从右上方到左下方,在横纵坐标整点开始往右上方走,遇到
R
,累加到变量中,遇到T
变量清零,最后累加到答案中
对于直线上的整点的操作,这里有个小技巧:先将斜率的分子分母化为最简,再在某个直线上的整点横纵坐标进行加减操作
- 时间复杂度:\(O(qnm)\)
代码
// Problem: 银杏林
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/32701/C
// Memory Limit: 262144 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1005;
int n,m,q;
char g[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>(g[i]+1);
cin>>q;
while(q--)
{
int res=0;
int x,y;
cin>>x>>y;
if(x!=0&&y!=0)
{
int d=__gcd(abs(x),abs(y));
x/=d,y/=d;
}
if(x==0)
{
if(y>0)
{
for(int i=1;i<=m;i++)
for(int j=n;j>=1;j--)
{
if(g[j][i]=='R')res++;
if(g[j][i]=='T')break;
}
}
else
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(g[j][i]=='R')res++;
if(g[j][i]=='T')break;
}
}
}
else if(y==0)
{
if(x>0)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(g[i][j]=='R')res++;
if(g[i][j]=='T')break;
}
}
else
{
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
{
if(g[i][j]=='R')res++;
if(g[i][j]=='T')break;
}
}
}
else if(x>0)
{
swap(x,y);
for(int i=0;i<=m;i++)
{
int x0=n+1,y0=i;
while(x0>=1&&y0<=m)
{
if(x0<=n)
{
if(g[x0][y0]=='R')res++;
if(g[x0][y0]=='T')break;
}
x0-=x,y0+=y;
}
}
for(int i=1;i<=n;i++)
{
int x0=i,y0=0;
while(x0>=1&&y0<=m)
{
if(y0>=1)
{
if(g[x0][y0]=='R')res++;
if(g[x0][y0]=='T')break;
}
x0-=x,y0+=y;
}
}
}
else
{
x=-x,y=-y;
swap(x,y);
int t=0;
for(int i=0;i<=m;i++)
{
int x0=n+1,y0=i;
t=0;
while(x0>=1&&y0<=m)
{
if(x0<=n)
{
if(g[x0][y0]=='R')t++;
if(g[x0][y0]=='T')t=0;
}
x0-=x,y0+=y;
}
res+=t;
}
for(int i=1;i<=n;i++)
{
int x0=i,y0=0;
t=0;
while(x0>=1&&y0<=m)
{
if(y0>=1)
{
if(g[x0][y0]=='R')t++;
if(g[x0][y0]=='T')t=0;
}
x0-=x,y0+=y;
}
res+=t;
}
}
cout<<res<<'\n';
}
return 0;
}