2022牛客寒假算法基础集训营6题解 ABCDEFGHIJ
Posted quinn18
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022牛客寒假算法基础集训营6题解 ABCDEFGHIJ相关的知识,希望对你有一定的参考价值。
文章目录
- A 回文大师 哈希二分/kmp 【补】
- B 价值序列 计数
- C 数组划分 栈/并查集 【补】
- D 删除子序列 计数
- E 骑士 贪心
- F +-串 分类讨论
- G 迷宫2 思维+最短路 【补】
- H 寒冬信使2 博弈【补】
- I A+B问题 进制模拟
- J 牛妹的数学难题 数学
- 总结
比赛链接
题解
A 回文大师 哈希二分/kmp 【补】
题目链接
题意:
翻译过来就是
求每一个前缀串 在原串的反串中有多少个相同的子串
题解:
可以构造一个反串
枚举反串开始的位置 再二分去找最长的和原前缀匹配(哈希O1判断)的在原串的位置,这个位置之前的所有前缀 的ans都+1,最后答案用差分维护
#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int N=1e6+5;
const int M=1e9+7;
const int qwq=23333;
int p[N];
int a[N];
int ans[N];
int haa[N],hbb[N];
int get(int a,int b)
return hbb[b]-hbb[a-1]*p[b-a+1];
signed main()
ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n;
cin>>n;
p[0]=1;
for(int i=1; i<=n; i++)
p[i]=p[i-1]*qwq;
for(int i=1; i<=n; i++)
cin>>a[i];
haa[i]=haa[i-1]*qwq+a[i];
hbb[n-i+1]=hbb[n-i+1+1]*qwq+a[i];
reverse(a+1, a+1+n);
for(int i=1; i<=n; i++)
hbb[i]=hbb[i-1]*qwq+a[i];
for(int i=1;i<=n; i++)
int l=1,r=n-i+1;
while(l<=r)
int mid=(l+r)/2;
if(get(i, i+mid-1)==haa[mid])
l=mid+1;
else r=mid-1;
ans[1]++;
ans[l]--;//求出来的是l==mid+1
for(int i=1; i<=n; i++)
ans[i]+=ans[i-1];
cout<<ans[i]<<" ";
cout<<endl;
return 0;
B 价值序列 计数
题目链接
题意:
题解:
删除一个数,价值必定不增
上升的序列和下降的序列之间的数 是可取可不取的 每一个数对答案的贡献是 2^(sum)
而波峰的那个值和波谷至少保留一个不然答案会减少 对答案的贡献是(2^sum)-1
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define endl '\\n'
using namespace std;
const int N=2e7+5;
const int M=998244353;
struct node
int v, sum;
b[N];
int p[N],a[N];
ll ksm(ll a,ll p)ll res=1;while(p)if(p&1)res=res*a%M;a=a*a%M;p>>=1;return res;
signed main()
ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t; cin>>t;
while(t--)
int n; cin>>n;
for (int i=1; i<=n; i++)
cin>>a[i];
b[i].sum=0;
int cnt=0;
b[++cnt].v=a[1];b[cnt].sum=1;
for(int i=2; i<=n; i++)
if(a[i]!=a[i-1]) b[++cnt].v=a[i];
b[cnt].sum++;
int ans=1;
for (int i=1; i<=cnt; i++)
if(i==cnt||i==1)ans=(ans%M*(ksm(2, b[cnt].sum)-1+M)%M)%M;
else if (b[i-1].v<b[i].v&&b[i].v<b[i+1].v||b[i-1].v>b[i].v&&b[i].v>b[i+1].v)
ans=(ans%M*(ksm(2, b[i].sum)+M)%M)%M;
else
ans=(ans%M*(ksm(2, b[i].sum)-1+M)%M)%M;
ans%=M;
cout<<ans<<endl;
return 0;
C 数组划分 栈/并查集 【补】
题目链接
题意:
求某个区间的有几个子数组的每个前缀和都大于等于0 且划分的数组尽量少
题解:
从每个位置(记为l),出发去找第一个
s
u
m
[
r
]
−
s
u
m
[
l
−
1
]
<
0
sum[r]-sum[l-1]<0
sum[r]−sum[l−1]<0 的 r 位置,题目里 r 是不包含的,这个
[
l
,
r
)
[l, r)
[l,r) 就是最大的满足所有前缀和都大于等于
0
0
0 的划分区间
就是对于每个
l
l
l 去找第一个
s
u
m
[
r
]
<
s
u
m
[
l
−
1
]
sum[r]<sum[l-1]
sum[r]<sum[l−1] 的 r
前缀和sum=5 1 3 8 7 6
一开始 6
i=6时 6 7
i=5时 6 7 8 保留这些值是因为前面可能出现 7 也有可能出现 9,7的第一个最小的是6 而9的第一个小于的是8
i=4时 3
i=3时 1
i=2时 1 5
所以我们用单调栈维护每个i对应的r,从右向左循环,若sumi<栈顶元素,那么栈顶元素不能成为左边的第一个小于就弹出,每i次栈内都是这个元素x划分的下一个位置
f
x
fx
fx 和 下一个位置
f
(
f
x
)
f(fx)
f(fx) 和下一个位置
f
(
f
(
f
x
)
)
f(f(fx))
f(f(fx)),刚好是对
i
i
i 来说到
n
n
n 的划分的区间个数且是单调递减的 这样我们维护值改为维护下标 那么当枚举到题目要求查询的
l
l
l 我们就可以二分去找 最后一个小于
r
r
r的下标
m
i
d
mid
mid 答案就等于
c
n
t
−
m
i
d
+
1
cnt-mid+1
cnt−mid+1
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e6+5;
const int M=1e9+7;
const int qwq=23333;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar()
if(head==tail)
int l=fread(buffer,1,BufferSize,stdin);
tail=(head=buffer)+l;
return *head++;
inline int read()
int x=0,f=1;char c=Getchar();
for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
return x*f;
void print(int x)
if(x>9) print(x/10);
putchar(x%10|'0');
int l[N], r[N];
int sum[N];
int ans[N];
int st[N];
signed main()
ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;
t=read();
while(t--)
int n, q;
n=read();
q=read();
for(int i=1; i<=n; i++)
int x=read();
sum[i]=sum[i-1]+x;
for(int i=1; i<=q; i++)
l[i]=read();
r[i]=read();
int m=q;
int cnt=0;
st[++cnt]=n;
for(int i=n; i>=1; i--)
while(cnt&&sum[st[cnt]]>=sum[i-1])
cnt--;
st[++cnt]=i-1;
while(m&&l[m]==i)
int ll=1, rr=cnt;
while(ll<rr)
int mid=ll+rr>>1;
if(st[mid]<r[m])rr=mid;
else ll=mid+1;
ans[m]=cnt-(ll)+1;
m--;
for(int i=1; i<=q; i++)
print(ans[i]);
putchar('\\n');
return 0;
D 删除子序列 计数
题目链接
题意:
题解:
因为T字符串没重复的字符 那么从后往前循环找 这个字符的后一个字母有没有
#include<bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
const int N=2e6+5;
const int M=998244353;
int a[N], b[N],sum[N];
signed main()
ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--)
int n, m;
cin>>n>>m;
string s;
string t;
cin>>s>>t;
for(int i=1; i<=26; i++) sum[i]=0;
map<char, char> mp;
for (int i=0; i<m-1; i++) mp[t[i]]=t[i+1];
for2022牛客寒假算法基础集训营6 全部题解