1. 程式人生 > 其它 >牛客IOI周賽24-普及組

牛客IOI周賽24-普及組

比賽連結

牛客IOI周賽24-普及組

B.數字串

題目描述

給定兩個正整數 \(L, R\) ,還有一個數字串 \(s\) (由 \(0 \sim 9\) 等數字組成)。
問現在數字串裡面有多少個不同子段 \(s[l, r]\) 組成的數(把子串這一段提出來形成的數字) \(k\) 滿足:

\[L<=k<=R \]

給定的 \(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;
}