zoukankan      html  css  js  c++  java
  • 题解 GDFZOJ 2020普转提十连测day4

    零、写在前面

    看原比赛戳这儿

    本次比赛题目难度:普及(不超过第三题)

    这次比赛主要目的是考查对数据结构的掌握程度

    一、T1 序列

    看原题戳这儿

    1、审题

    给出一个长度为(n)的整数序列 (,)要求删掉一个连续子串后序列中没有相同元素 (,) 请问至少要删掉多长的子串(?)

    乍一看很多人肯定会说,这不是一道水题吗?直接把所有相同的去掉不就行了吗?

    这就代表你没有认真地审题。

    再读一次题:给出一个长度为(n)的整数序列 (,)要求删掉一个连续子串后序列中没有相同元素 (,) 请问至少要删掉多长的子串(?)

    所以肯定不能直接去掉啦。

    再看一看数据范围:(1le nle1000,0le a_i le10^9)

    (emm······)看来桶也不能用了呢,那应该怎么办呢,事实上这道题需要用到(map)(Hash)

    2、概念

    已经会了(Hash)(map)的同学请跳过本部分(······)

    Map

    推荐文章:C++ map用法总结(整理)C++中的STL中map用法详解

    Hash

    推荐文章:C++:哈希C++ STL中哈希表 hash_map从头到尾详细介绍

    3、做题

    这道题有三个难点:起点是啥,终点是啥,怎么判断是否满足条件?

    起点是啥? 对于这个问题,我们只需要枚举区间起点,再看在起点之前有没有重复的元素,如果有重复的就直接退出循环

    终点是啥 刚才我们枚举了起点,但如果没有重复的呢,这时我们要枚举终点

    怎么判断是否满足条件 当我们求出了起点和终点之后,我们就可以看最短能有多短(终点取最前值)了

    时间复杂度(O(n))

    4、代码

    #include<bits/stdc++.h>
    using namespace std;
    int a,b,c,d,ans=1e9;
    int aa[1000001];
    int main()
    {
    	scanf("%d",&a);
    	for(int i=1;i<=a;++i) scanf("%d",aa+i);
    	for(int i=1;i<=a;++i)
    	{
    		int tmp=a-i+1;
    		map<int,int> hash;
    		for(int j=1;j<i;++j)
    		{
    			if(hash[aa[j]])
    			{
    				printf("%d",ans);
    				return 0;
    			}
    			hash[aa[j]]++;
    		}
    		for(int j=a;!hash[aa[j]]&&j>=1;j--) hash[aa[j]]++,tmp--;
    		ans=min(ans,tmp);
    	}
    	printf("0");
    	return 0;
    }
    

    二、T2 数字

    看原题戳这儿

    1、审题

    给出两个整数 (n,m),求(1)(n)的所有整数中,能被(m)整除的整数的个位数字之和。输入包含多组数据。

    数据范围:(1le m,nle10^{16},T le 100)

    又是一道题意简单的题,不过(n)的范围太过于硕大,所以必须思考(O(1))的算法

    2、规律

    经过一番的思(da)考(biao),我们发现对于(forall m)都有:(m)的倍数的个位数字都是循环的,而且循环节(不保证最小)都是10。

    找到了以上的规律之后,我们就可以开始愉快的做题了

    3、做题

    首先我们可以暴力枚出循环节是什么(反正也就10个),开一个数组(sum)记录循环节,$ sum_i(表示循环节中的第)i(个的个位是多少,然后将这些全部都加起来为)c$,表示一个循环节的个位的总和是多少。

    然后我们就可以判断区间([1,n])之间有多少个循环节,这个很容易求出,就是(n/m),然后再乘上一个循环节的个位的总和,就可以求出来了。

    可是这就完了吗?

    并不是,我们算一算现在我们已经求了几个数,发现目前只求出了(n / m * m)个,我们知道(c++)(n / (10 * m) * (10 * m))并不一定等于(n),所以一定还有遗漏的,那是多少呢,当然就是(n-n/(10 * m) * (10 * m))啦,因为现在剩下的这些数的数量已经小于(m)了,所以我们用((n-n/(10 * m) * (10 * m))/m)就可以算出来还剩多少个没算啦。

    综合以上内容我们完美地推出了一个(O(1))的公式:

    [ans=c * (a/(10 * b))+sumlimits_{i=1}^{(n-n/(10 * m) * (10 * m))/m}sum_i ]

    于是,这道题就完美的解决啦!

    4、代码

    #include<bits/stdc++.h>
    using namespace std;
    long long int a,b,c,d,ans;
    long long int aa[1000001];
    int main()
    {
    	int T;
    	scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%lld%lld",&a,&b);c=d=ans=0;
    		for(int i=1;i<=10;++i) aa[i]=b*i%10;
    //		for(int i=1;i<=10;++i) printf("%d ",aa[i]);printf("
    ");
    		for(int i=1;i<=10;++i) c+=aa[i];
    		ans=(c*(a/(10*b))),a=a-a/(10*b)*(10*b);
    //		printf("%lld %lld %lld %lld ",a,10*b,a/(10*b),c);
    		d=a/b;
    		for(int i=1;i<=d;++i) ans+=aa[i];
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    T3 有趣的数

    看原题戳这儿

    1、审题

    将题目简化一下之后就是:对于一个正整数,对于其所有的数位,当且仅当它恰好有一位与其他位不同,则称这个数为有趣的数,求([L,R])中有多少个有趣的数。

    数据范围:(100le Lle Rle 10^{16})

    首先我们看到了对于有趣的数的定义,发现判断一个数是不是有趣的数并不难,但是这道题的(n)又是过于硕大,所以必须考虑更快的方法

    2、做题

    因为数据过大,所以直接暴力枚举每一个数再判断是肯定不行的,那我们枚举什么呢?

    答案是枚举有趣的数。

    我们需要开四层循环,第一层枚举位数,第二层枚举其他位,第三层枚举那个特殊的一位,最后一层枚举特殊的那一位在哪儿,这样的话我们就可以求出一个有趣的数啦,然后我们只需要再判断一下就可以了,时间复杂度(O(17 * 10 * 10 * 17 * 17)),十分之小

    3、代码

    #include<bits/stdc++.h>
    using namespace std;
    long long int a,b,c,d,ans;
    bool ck(long long int l,long long int ding,long long int bian,long long int wei)
    {
    	long long int sum=0;
    	if(bian==0&&wei==1) return false;
    	if(ding==0&&wei!=1) return false;
    	for(long long int i=1;i<=l;++i)
    	{
    		sum*=10;
    		if(i==wei) sum+=bian;
    		else sum+=ding;
    	}
    	if(a<=sum&&sum<=b) return true;
    	else return false;
    }
    int main()
    {
    	scanf("%lld %lld",&a,&b);
    	for(long long int i=3;i<=17;++i)
    	for(long long int j=0;j<=9;++j)
    	for(long long int k=0;k<=9;++k)
    	{
    		if(j==k) continue;
    		for(long long int l=1;l<=i;++l)
    		if(ck(i,j,k,l)) ans++;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    T4 欢乐ABC

    看原题戳这儿

    1、审题

    (PiPi)随手在纸上写下了一个字符串,然后他突然想到了一个问题,在这个字符串中,有多少个非空连续子串,其中(A)字符的数量,(B)字符的数量和(C)字符的数量三个量相等。

    数据范围:(|S|le10^6)

    乍一看到这个数据,我们就知道只能做(O(n))的算法了

    2、做法

    首先,我们需要对(A B C)三个字母都求一次前缀和,分别命名为A、B、C

    经过一番的思考,我们发现若S是一个合法的非空子串,起始点和终止点为(i)(j),必须满足$ A_{i-1}-B_{i-1}=A_j-B_j(且) B_{i-1}-C_{i-1}=B_j-C_j(,于是我们就对于)forall i(,都将)(A_i-B_iB_i-C_i)$看成一个二元组,然后
    用map求出有多少对相同的二元组,最后记入答案就行啦

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int a,b,c,d,ans;
    int aa[100001],bb[100001];
    char s[100001];
    int main()
    {
    	cin>>s;a=strlen(s);
    	for(int i=0;i<a;++i) aa[i+1]=s[i]-'A'+1;
    //	for(int i=1;i<=a;++i) printf("%d ",aa[i]);printf("%d
    ",a);
    	for(int i=3;i<=a;i+=3)
    	{
    		memset(bb,0,sizeof(bb));
    		for(int j=1;j<=i;++j) bb[aa[j]]++;
    		for(int j=i+1;j<=a;++j)
    		{
    			bb[aa[j]]++;
    			bb[aa[j-i]]--;
    //			printf("%d %d %d %d %d
    ",i,j,bb[1],bb[2],bb[3]);
    			if(bb[1]==bb[2]&&bb[3]==bb[2]) ans++;
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    /*
    ABACABACB
    */
    

    五、写在后面

    这次题目总体不难,但是一定要记得开(long long),一定要记得开(long long),一定要记得开(long long),重要的事情说三遍!!!

    [ ext{十年}OI ext{一场空,不开}long long ext{见祖宗} ]

    完美撒花!!!

  • 相关阅读:
    Python 元胞自动机模拟——生命游戏
    ()python画动态图——plt.ion动图使用,训练过程展示
    Python——因子分析(KMO检验和Bartlett's球形检验)
    bind(),unbind(),hover(),toggle(),animate()
    多计算机通信中的时间同步问题
    如何创建自定义尺寸的空白地图
    ROS中的珊格地图——nav_msgs::OccupancyGrid
    c++使用eigen库,矩阵维度错误
    Eigen 求最小二乘
    Mybatis 框架下 SQL 注入攻击的 3 种方式,真是防不胜防!
  • 原文地址:https://www.cnblogs.com/zhnzh/p/13404694.html
Copyright © 2011-2022 走看看