HGOI8.16集训题解

题解

日常爆零orz


第一题——玛雅文字(mayan)

【题目描述】

  • 解读玛雅文字向来不简单,因为单词中的字母顺序可以是任意排列的。今天,科研团队找到了你来解决一个简化过的问题——在给定的一段玛雅文字 S 中,求出给定的单词 T 出现了几次,并保证 S 和 T 均由大小写字母构成。

  • 限制

  • 1s 32M
  • 1≤|T|≤ 3000,|T|≤|S|≤ 3,000,000

    【input.ex】

    4 11
    cAda
    AbrAcadAbRa

    【output.ex】
    2

    子串 Acad 和 cadA 均是 cAda 的排列,因此一共出现了 2 次。


  • 我刚开始看成是直接上来KMP但由于可以排列,那就只要统计区间内各个字母的数量就可以了。三十万*56貌似可以水过
  • 孩子们注意边界啊…多打一个=,少了90分orz

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void fff(){
    freopen("mayan.in","r",stdin);
    freopen("mayan.out","w",stdout);
}
int n,m;
char s[3000010],t[3010];
int cnt1[130],cnt2[130];
int main(){
//  fff();
    n=read(),m=read();
    scanf("%s",t);
    scanf("%s",s);
    memset(cnt1,0,sizeof cnt1);
    for (int i=0;i<n;i++){
        cnt1[t[i]]++;
    }
    int ans=0;
    for (int i=0;i<m;i++){
        cnt2[s[i]]++;
        if(i>=n-1){
            cnt2[s[i-n]]--;
            bool flag=false;
            for (int j='A';j<='z';j++){
                if(cnt1[j]!=cnt2[j]){
                    flag=true;
                    break;
                }
            }
            if(!flag) ans++;
        }
    }
    cout<<ans;
}

第二题——奇怪的字符串(Training )

【题目描述】

  • 考虑字符串 s 仅由小写字母组成,例如 “abba”。定义 W(s) 为 s 所有本质不同的连续子串的集合,例如 W(“abba”) = { “a”,”b”,”ab”,”ba”,”bb”,”abb”,”bba”,”abba” }。定义 Y(s) 为 s 所有本质不同的非连续子串的集合,例如 Y(“abba”) = W(“abba”) ∪ { “aa”,”aba” },显然 W(s) 是 Y(s) 的子集。

  • 对于一些奇怪的字符串 s 满足 W(s) = Y(s),例如 “abba” 就不是奇怪的,但 “abb” 是奇怪的因为 W(s) = Y(s) = { “a”,”b”,”ab”,”bb”,”abb” }。现在小明有一个字符串 s,请你求出 W(s) 中有多少个字符串是奇怪的?

  • 注意:集合中的所有元素互不相同

  • 限制

  • 1s 256M

  • 1≤|s|≤ 200,000

    【 输入样例】

    abba

    【 输出样例】

    7
    abba 的所有连续子串中,除了 abba 以外都是 ” 奇怪的 “


  • 通过观察可以发现,s 是 ” 奇怪的 ” 的条件是形如 aaaa 或 aaabbbb。
  • 对于连续的一段字母进行 “ 压缩 ” 扫描线处理,记录同种字母 k 的最大连续长度、字母 k1 在固定长度 c1 后面接字母 k2 的最大长度,以将本质不同(这是最麻烦的地方)的字符串区分开来。这些均可用数组和 map 维护(容易 MLE),并最后一边维护最大值一边统计答案。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
#define LL long long
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void fff(){
    freopen("strange.in","r",stdin);
    freopen("strange.out","w",stdout);
}
char s[200100],k1,k2;
int n,c1,c2;
struct node{
    int k1,k2,c1,c2;
}d[200100];
map<pair<int,int>,int> b[30];
int a[27];
int main(){
//  fff();
    scanf("%s",s+1);
    n=strlen(s+1);
    int pos=0,cnt=0;
    s[0]=s[1];
    for (int i=1;i<=n;i++){
        while (pos<n){
            if(s[pos]!=s[pos+1]&&cnt==1) break;
            if(s[pos]!=s[pos+1]){
                cnt++;
                k2=s[pos+1];c2++;
            }else if(c2) c2++;else k1=s[pos],c1++;
            pos++;
        }
        d[i]=(node){k1-'a'+1,k2-'a'+1,c1,c2};
        a[k1-'a'+1]=max(c1,a[k1-'a'+1]);
        if(s[i]!=s[i+1]){
            cnt--;
            k1=k2;
            c1=c2;
            c2=0;
        }else c1--;
    }
    LL ans=0;
    for (int i=1;i<=26;i++)ans+=a[i];
    for (int i=1;i<=n;i++){
        if(d[i].c2==0) continue;
        int tmp=b[d[i].k1][make_pair(d[i].c1,d[i].k2)];
        b[d[i].k1][make_pair(d[i].c1,d[i].k2)]=max(tmp,d[i].c2);
        ans+=max(0,d[i].c2-tmp);
    }
    printf("%lld\n",ans);
}

第三题——超级跳棋(Training)

【题目描述】

  • 小明是今年超级跳棋比赛的裁判,每轮有三名选手参加,结束时统计的分数一定是正整数,形如 a:b:c。小明的任务是在一块特殊的计分板上展示分数,他一共准备了 n n 块写有正整数 x1x2xn 的卡片,可供填写在 a、b、c 的位置上。此外,小明了解到超级跳棋的规则,他发现 a、b、c 之间最多相差 k k 倍,例如 ca>k 就是不合法的分数。为了检验他准备得是否充分,你需要计算小明可以在计分板上摆放出多少种不同的分数,即 (a,b,c) 这样的三元组有多少个。

    【输入样例】

    5 2

    1 1 2 2 3

    【输出格式】

    9

    【样例解释】

    小明可以摆出的 a:b:c 有以下这些:1:1:2、1:2:1、2:1:1、1:2:2、2:1:2、2:2:1、2:2:3、2:3:2、3:2:2。由于 k=2 k = 2 ,1 和 3 不能同时出现。


  • 首先将 xi x i 排序,对于固定的最小值 xi x i ,a、b、c 三个数必然在 xi x i xj x j 中选(即 xj+1xi>k x j + 1 x i > k ),当然对于 i i 可以扫描线快速求得 j
  • 假设 xi x i 必然取,然后可以根据 xi x i 选 1 个还是 2 个还是 3 个,分类讨论并用乘法原理统计答案。需要同时维护当前 xi x i xj x j 有几个是只出现了 1 次、有几个是出现了至少 2 次的(用 map 记录一个数出现了几次)。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#define LL long long
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void fff(){
    freopen("super.in","r",stdin);
    freopen("super.out","w",stdout);
}
int n;
int tt;
int a[100010],b[100010],sz=0;
map<int,int> m;
int main(){
//  fff();
    n=read(),scanf("%d",&tt);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        m[a[i]]++;
    }
    sort(a+1,a+n+1);
    for (int i=1;i<=n;i++){
        if(a[i]!=a[i-1]) b[++sz]=a[i];
    }
    LL ans=0;
    int pos=0,c1=0,c2=0;
    for (int i=1;i<=sz;i++){
        while (pos<sz&&b[pos+1]<=(LL)b[i]*tt){
            pos++;
            if(m[b[pos]]>=2) c1++;
            else c2++;
        }
        int v=0;
        if(m[b[i]]>=2) v++;
        int s=c1+c2-1;
        ans+=3*(c1-v);
        if(s>1) ans+=3ll*s*(s-1);
        if(m[b[i]]>=2)ans+=3*s;
        if(m[b[i]]>=3) ans++;
        if(v) c1--;else c2--;
    }
    printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值