CSP-J模拟赛五补题报告

Coduck S14754        赵廷赫

一,比赛情况

一,四题0分,二题30分,三题20分,总分50分。

二,赛中概况

先做了第一题,会,算法也对,但是运行出了问题,改了45分钟,还是不对。然后去做第二题,算法只在部分情况下正确。第三题方法会也不会,用暴力得了了二十分,第四题,完全不会+骗分失败,0分。

三,补题报告

T1:

题目:

重复判断

时间限制:1秒        内存限制:256M

题目描述

小可需要判断一个字符串a,是否由另一个字符串b生成出来的,所谓的生成,其实就是把字符串b重复若干次,即:判断字符串a是否是字符串b重复若干次得到的。

输入描述

第一行:输入一个数字 t,表示共有 t 组询问。

接下来 t 行每行两个字符串 a 和 b。

输出描述

输出 t 行,每行输出 YESNO

输入样例

2

addaddadd add

abcd bcda

输出样例

YES

NO

数据描述

10%的数据下:字符串长度均<11。

题目分析:

判断一个字符串a是否可以由一个或多个字符串b连接到一起得到。

可以使用模拟法,模拟b生成a的过程。

AC代码:

#include<bits/stdc++.h>
using namespace std;
string a,b,s;
bool cmp(string a,string s)
{
    int al=a.size(),sl=s.size();
    if(al!=sl)
    {
        return 0;
    }
    for(int i=0;i<sl;i++)
    {
        if(s[i]!=a[i])
        {
            return 0;
        }
    }
    return 1;
}
int main()
{
    int t,al,bl,sl;
    cin>>t;
    bool bo,f[t+10];
    for(int i=1;i<=t;i++)
    {
        cin>>a>>b;
        bl=0;
        s="";
        al=a.size();
        bl=b.size();
        sl=0;
        if(al<bl)
        {
            f[i]=0;
            break;
        }
        if(al==bl)
        {
            if(a==b)
            {
                f[i]=1;
                break;
            }
            else
            {
                f[i]=0;
                break;
            }
        }
        while(sl<=al)
        {
            s+=b;
            sl+=bl;
            if(cmp(a,s))
            {
                f[i]=1;
                bo=1;
                break;
            }
        }
        if(bo==0)
        {
            f[i]=0;
        }
    }
    for(int i=1;i<t;i++)
    {
        if(f[i])
        {
            cout<<"YES"<<endl;
        }
        else
        {
            cout<<"NO"<<endl;
        }
    }
    if(a[t])
    {
        cout<<"YES";
    }
    else
    {
        cout<<"NO";
    }
    return 0;
}

T2:

歪果仁学乘法

时间限制:1秒        内存限制:128M

题目:

题目描述

歪果仁不太会乘法,原因是他们被不过九九乘法表,小可听到之后,提出了一种不需要九九乘法表也可以计算乘法的方式,对于a × b:

1.将a,b的每一位上的数码画成线,不同位之间分隔开。
2.a 和 b 的方向垂直画出。
3.数出每个方向上交点的个数,即是 c 对应位置上的数码。

样例图给出了计算12 ×13的方法:

1.红色线分别画出 1 条和 2 条;
2.蓝色线分别画出 1 条和 3 条;
3.数出红、蓝色线的交点个数,依次为 1,5,6 个;
4.得到答案:12 × 13 = 156。

给出两个数字 a, b,求它们的乘积时交点的总个数是多少。

输入描述

输入两个正整数 a和 b 。

输出描述

输出交点的总个数。

输入样例

12 13

输出样例

12

样例图如下:


一共12个交点。

数据描述

对于100%的数据:1<=a,b<=99。

测试点1~3:1<=a,b<=9。

测试点4~6:计算 a × b 时,不会进位。

测试点7~10:无特殊限制。

题目解析:

  解题思路:将a,b的各位分别相乘,答案等于和。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a,b,ag,bg,as,bs,ans;
    cin>>a>>b;
    if(a<10&&b<10)
    {
        cout<<a*b;
        return 0;
    }
    ag=a%10;
    as=a/10;
    bg=b%10;
    bs=b/10;
    ans=bs*as+bs*ag+bg*as+bg*ag;
    cout<<ans;
    return 0;
}

时间复杂度为O(1)。

T3:

题目:

去重求和

时间限制:3秒        内存限制:256M

题目描述

小可有一个长度为 n 的序列 a_ia​i​​。

他定义sum(l,r)sum(l,r),为a[l]a[l] ~ a[r]a[r] ,这些数去重之后的和。

请求出

输入描述

第一行:输入一个正整数 n ,表示序列长度。

第二行:输入n个整数,表示序列 a 。

输出描述

输出答案,答案对10^9+710​9​​+7取模。

输入样例

4
1 1 4 5

输出样例

51

样例说明

sum(1,1)=1,sum(1,2)=1,sum(1,3)=5,sum(1,4)=10sum(1,1)=1,sum(1,2)=1,sum(1,3)=5,sum(1,4)=10

sum(2,2)=1,sum(2,3)=5,sum(2,4)=10sum(2,2)=1,sum(2,3)=5,sum(2,4)=10

sum(3,3)=4,sum(3,4)=9sum(3,3)=4,sum(3,4)=9

sum(4,4)=5sum(4,4)=5

因此答案为 1 + 1 + 5 + 10 + 1 + 5 + 10 + 4 + 9 + 5 = 51。

数据描述

对于100%的数据:1<=n<=5*10^5,1<=a[i]<=10^9

题目解析:

解题思路:

(1)找特殊情况:

所有数据都不相同,总结规律。

(2)推广到一般情况:

将区间统计变成贡献统计,利用求贡献的算法解决问题。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
typedef long long ll;
const int mod = 1e9 + 7;
int n, a[N];
map<int,int>mp;
int l[N], r[N];
int main() 
{
    cin >> n;
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        mp[a[i]] = 1;
    }
    mp.clear();
    for(int i = 1; i <= n; ++i) 
    {
        l[i] = mp[a[i]] + 1;
        mp[a[i]] = i;
    }
    ll ans = 0;
    for(int i = 1; i <= n; ++i) 
    {
        ans += 1ll * a[i] * (i - l[i] + 1) % mod * (n - i + 1) % mod;
        ans %= mod;
    }
    cout << ans << endl;
    return 0;
}

T4:

题目:

点集操作

时间限制:3秒        内存限制:256M

题目描述

小可在学习图论,他有一个有向无环图,他想知道对这个图做任意次modify(i,j)modify(i,j)操作之后的图中剩余的最小点数,其中1\leq i,j\leq n1≤i,j≤n,其中modify(i,j)modify(i,j)为一次操作:

1.任选不同的两个点i,ji,j
2.称 A_iA​i​​ 为 ii 能到达的所有点组成的点集, A_jA​j​​ 为 jj 能到达的所有点组成的点集 。(注意:每个点可以到达的点集包含这个点本身)
3.设 B 为一个最大的点集,满足 B 既是 A_iA​i​​ 的子集,又是A_jA​j​​的子集 。
4.将 B 在图中变成一个新点,B 内的所有边全部删除。点集 B 以外的点与点集 B 以内的点的连边关系转移到新点上。

输入描述

第一行两个数 n,m。

接下来 m 行每行两个数表示一条有向边 。

输出描述

一行一个数表示最小剩余点数。

输入样例

5 6
2 1
5 1
2 3
4 3
5 4
4 1

输出样例

3
题目解析:

样例画图可得,把有入度的相关点的重叠点,变成一个大点B,是一次modify操作。

在一个单向链上,只需要让链头的两个点进行一次操作就可以将整个链变成两个点。
所以每次操作可以在一条所含点数超过 2 的单向链上进行,直至不能继续操作,剩下的点的个数即为图中剩余的最小点数。
由上面操作的特殊性可以知道所有入度为 0 的点删不掉,所有满足所有入边对应点入度为 0 的点也删不掉,可以将这些点(所有满足所有入边对应点入度为 0 的点)看成新点。统计这些点个数即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int n,m,x[MAXN<<1],y[MAXN<<1],in[MAXN];
int head[MAXN],num,ans;
bool b[MAXN],vis[MAXN];
vector<int>v;
queue<int>q;
struct node
{
    int to,next;
}e[MAXN<<1];
void add(int u,int v)
{
    e[++num].to=v;
    e[num].next=head[u];
    head[u]=num;
}
int main()
{
    cin >> n >> m;
    for(int i=1;i<=m;++i)
    {
    cin >> x[i] >> y[i];
    add(x[i],y[i]);
    in[y[i]]++;
    }
    for(int i=1;i<=n;++i)
    {
        if(!in[i])
        {
            v.push_back(i);
            ans++;
        }
    }
    for(int i=1;i<=m;++i)
    {
        if(in[x[i]])b[y[i]]=1;
    }
    for(int i=0;i<v.size();++i)
    {
        for(int j=head[v[i]];j;j=e[j].next)
        {
            if(!b[e[j].to])
            {
                b[e[j].to]=1;
                ans++;
            }
        }
    }
    cout << ans;
    return 0;
}

四,比赛总结

比赛1题难度不太高,2题算法很容易忽略进位3题算法难度还可以,但是思维难度高,很难想到正确算法,4题骗分都很难,更别提AC了。

这套题不难,主要是1题运行出了问题,2题算法不完全正确。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值