2022牛客寒假算法基础集训营6 全部题解

Posted quinn18

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022牛客寒假算法基础集训营6 全部题解相关的知识,希望对你有一定的参考价值。

文章目录


比赛链接
题解


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[l1]<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[l1] 的 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 cntmid+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];
        for(int i=n-1; i>=0; i--) 
            if(s[i]==t[m-1]) 
                sum[s[i]-'a'+1]++2022牛客寒假算法基础集训营3全部题解

2022牛客寒假算法基础集训营3全部题解

2022牛客寒假算法基础集训营 5 全部题解

2022牛客寒假算法基础集训营 5 全部题解

2022牛客寒假算法基础集训营1全部题解

2022牛客寒假算法基础集训营6题解 ABCDEFGHIJ