牛客IOI周赛25-普及组
比赛链接
牛客IOI周赛25-普及组
B.学姐的编码1.0
题目描述
学姐最近喜欢上了编码,尤其是十六进制编码,但是学姐特别挑剔,在学姐眼中,只有逐位递增的编码才是一个优美的编码,比如12,58都是优美的编码,85,22则都不是优美的编码,现在学姐得到了一个编码串,她希望你告诉她该编码串里可查询到的所有不重复的优美的编码总个数,对于单个字符组成的编码,学姐总是认为这个编码是优美的,且优美的编码当中是允许存在前导零的
编码可查询的判定依据:在给定编码串\(s\)中删去任意kk位字符\((0≤k<|s|)\),剩下字符不改变顺序组成一个新的编码\(s1\),则认为\(s1\)可在\(s\)中查询到,如0102中可查询的编码有0,1,2,00,01,02,10,12,010,012,002,102,0102
输入描述:
一个字符串s表示所给十六进制编码串(1≤|s|≤1000000)
(保证所给编码串与标准十六进制一致,编码串中仅可能出现0-9与A-F,不会有多余字符出现)
输出描述:
一个数,表示不重复计算的情况下优美的编码的总个数
示例1
输入
001A
输出
7
说明
七种方案分别为
0,01,0A,01A,1,1A,A
备注:
对于10%的数据:1≤n≤5
对于30%的数据:1≤n≤10
对于100%的数据:1≤n≤1000000,其中另有10%保证字符串中0-9与A-F至少各出现一次
解题思路
思维,dp
-
状态表示:\(f[i]\) 表示以字符 \(i\) 结尾的方案数
-
状态计算:\(f[i]=1+ \sum_{j=0}^{i-1}f[j]\)
每当遇到一个字符 \(i\),算上本身 \(1\) 的贡献,再加上 \(0\sim i-1\) 的贡献,\(\color{red}{为什么这样是正确的?}\)当字符 \(i\) 只出现一次时,这个性质是显然的,当出现多次时,由于该字符前面还有该字符,中间会出现其他字符,如果出现小于当前字符的情况,原来的 \(f[i]\) 就不正确了,需要重新计算
- 时间复杂度:\(O(16n)\)
代码
// Problem: 学姐的编码1.0
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/32598/B
// Memory Limit: 524288 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=20;
LL f[N];
bool cnt[N];
int get(char c)
{
if(c>='0'&&c<='9')return c-'0';
else
return c-'A'+10;
}
int main()
{
string s;
cin>>s;
for(char c:s)
{
int i=get(c);
f[i]=1;
for(int j=0;j<i;j++)f[i]+=f[j];
}
LL res=0;
for(int i=0;i<16;i++)res+=f[i];
cout<<res;
return 0;
}
C.学姐的编码2.0
题目描述
学姐最近喜欢上了编码,尤其是十六进制编码,但是学姐特别挑剔,在学姐眼中,只有逐位递增的编码才是一个优美的编码,比如12,58都是优美的编码,85,22则都不是优美的编码,现在学姐得到了一个编码串,她希望你按照字典序从小到大输出该编码串里可查询到的所有不重复的优美的编码,对于单个字符组成的编码,学姐总是认为这个编码是优美的,且优美的编码当中是允许存在前导零的
编码可查询的判定依据:在给定编码串ss中删去任意k位字符\((0≤k<|s|)\),剩下字符不改变顺序组成一个新的编码\(s1\),则认为\(s1\)可在\(s\)中查询到,如0102中可查询的编码有0,1,2,00,01,02,10,12,010,012,002,102,0102
输入描述:
一个字符串s表示所给十六进制编码串(1≤|s|≤1000000)
(保证所给编码串与标准十六进制一致,编码串中仅可能出现0-9与A-F,不会有多余字符出现)
输出描述:
按字典序从小到大输出所有满足条件的编码
示例1
输入
001A
输出
0
01
01A
0A
1
1A
A
示例2
输入
00A1
输出
0
01
0A
1
A
备注:
对于10%的数据:1≤n≤5
对于30%的数据:1≤n≤10
对于100%的数据:1≤n≤1000000,其中另有10%保证字符串中0-9与A-F至少各出现一次
解题思路
dfs,二分,贪心
把每个字符出现的位置提取出来,再从前往后遍历字符串的开始字符,然后由于是递增的,从该字符后面遍历更大的字符,有贪心思想,肯定找越靠近且靠后的字符越好,可以二分该位置
- 时间复杂度:\(O(16\times 16\times logn)\)
代码
// Problem: 学姐的编码2.0
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/32598/C
// Memory Limit: 524288 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;
}
vector<int> a[16];
string s;
int get(char c)
{
if(c>='0'&&c<='9')return c-'0';
return c-'A'+10;
}
char inv_get(int x)
{
if(x>=0&&x<=9)return '0'+x;
return x-10+'A';
}
void dfs(string s,int id,int x)
{
cout<<s<<'\n';
for(int i=id+1;i<16;i++)
{
int pos=lower_bound(a[i].begin(),a[i].end(),x)-a[i].begin();
if(pos!=a[i].size())dfs(s+inv_get(i),i,a[i][pos]);
}
}
int main()
{
cin>>s;
int n=s.size();
s=' '+s;
for(int i=1;i<=n;i++)a[get(s[i])].pb(i);
for(int i=0;i<16;i++)
if(a[i].size())
{
string s;
s+=inv_get(i);
dfs(s,i,a[i][0]);
}
return 0;
}
D.迷阵
题目描述
学姐无意间走入了一个迷阵,迷阵可以看成一个 \(n \times n\) 的矩阵,每个位置有一个属性 \(w_{i j}\) ,学姐每次只能往相邻格点 移动,每个格点都可以被经过\(0\)次或任意次,已知对于两个格点 \(\left(x_{i}, y_{i}\right)\left(x_{j}, y_{j}\right)\) ,当且仅当 \(\left|x_{i}-x_{j}\right|+\left|y_{i}-y_{j}\right|=1\) 时认为这两点相邻,学姐打算从 \((1,1)\) 走到 \((n, n)\) ,但学姐走出迷阵后一段时间内会精神混乱,精神混乱的时间取决于 学姐在迷阵中所经过的所有格点 (包括起点和终点) 的权值的极差(最大值减去最小值)。现在学姐想知道走出迷宫精神混乱时间 \(T\) 最少是多少。
输入描述:
输入描述:
第一行一个正整数n(n≤100),表示迷阵大小
接下来n行,每行n个数,表示wij(0≤wij≤3000)
输出描述:
一个数,表示走到(n,n)时得到的最小的T
示例1
输入
5
1 1 3 6 8
1 2 2 5 5
4 4 0 3 3
8 0 2 3 4
4 3 0 2 1
输出
2
说明
1->1->2->2->0->2->0->2->1
路径上最大值M1=2,最小值M2=0,T=M1-M2=2
显然路径方案不唯一
备注:
对于15%的数据: 1≤n≤20 0≤wij≤100
对于45%的数据: 1≤n≤100 0≤wij≤100,其中有另5%保证1≤n≤100 0≤wij≤5
对于100%的数据: 1≤n≤100 0≤wij≤3000
解题思路
二分
假设当前极差为 \(x\),遍历最小值,如果存在这样一条满足范围内的路径,则说明该答案可能比当前极差小,否则不存在只能增大极差,扩大最小值和最大值的范围了,正好对应二分
另外,由于一个格点可以经过0次或任意次,只要找到这样一条路径即可,所以不需要回溯
- 时间复杂度:\(O(n^3log(3000))\)
代码
// Problem: 迷阵
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/32598/D
// Memory Limit: 524288 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=105;
int n,a[N][N],dx[]={-1,0,1,0},dy[]={0,1,0,-1};
bool f,v[N][N];
void dfs(int x,int y,int down,int up)
{
if(x==n&&y==n)
{
f=true;
return ;
}
for(int i=0;i<4;i++)
{
int nx=x+dx[i],ny=y+dy[i];
if(nx>=1&&nx<=n&&ny>=1&&ny<=n&&(!v[nx][ny])&&a[nx][ny]>=down&&a[nx][ny]<=up)
{
v[nx][ny]=true;
dfs(nx,ny,down,up);
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)cin>>a[i][j];
int l=0,r=3000;
while(l<r)
{
int mid=l+r>>1;
f=false;
for(int i=0;mid+i<=3000;i++)
{
int j=mid+i;
if(a[1][1]>=i&&a[1][1]<=j)
{
memset(v,0,sizeof v);
v[1][1]=true;
dfs(1,1,i,j);
if(f)break;
}
}
if(f)r=mid;
else
l=mid+1;
}
cout<<l;
return 0;
}