Codeforces 368B

news/2024/7/10 5:41:22 标签: codeforces, algorithm, 优化

说实话,做优化这个专题实在是磕磕绊绊的,交了很多TLE和WA,每道题一上手第一感觉是会做,然后按照往常的思路暴力求解。信心满满的交上去之后得到的只是超时的回复,于是我就开始找自己程序的问题,刚开始的时候,越改越乱,改到后面再提交就得到了WA的回复,然后就觉得特纳闷。于是第一道题就看了题解,当时看了之后是恍然大悟的感觉,然后自己也写了一份类似的代码匆匆忙忙提交后AC也就没管了,今天再看同样的这道题,竟然还是毫无头绪。说实话,心里还是挺失落的,不过加油总结,以后碰到类似的题目要会。废话少说,现在就上那几道题吧。

这是第一道题的题目描述:

给定一个数组a和q个查询,查询第i个数字及以后共有几个互不相同的数字。

Input

第一行是两个整数n和q(1<=n,q<=10^5),分别表示数组的长度和询问的次数。第二行是n个整数 a1a2, ..., an (1ai105表示数组内容。
接下来的q行表示q个询问,每询问是一个整数i(1<=i<=n)。

Output

对于每个询问,输出答案。

SampleInput

10 10
1 2 3 4 1 2 3 4 100000 99999
1
2
3
4
5
6
7
8
9
10

SampleOutput

6
6
6
6
6
5
4
3
2
1

这道题的题目很短,就短短的一句话:

给定一个数组a和q个查询,查询第i个数字及以后共有几个互不相同的数字。

一开始拿到这道题目的时候,我是按照常规暴力遍历的思路,每输入一个查询,然后用“桶的思想”便开始遍历从该数及以后共有多少个互不相同的数字,殊不知这样做其实重复了很多次的遍历,比如说第一次查询的数字是3,第二次是4.那么4后面的数字都是重复遍历。

而查询的次数高达10^5次方量级的,因此第一次的提交,因为太多的重复遍历操作,所以得到了一个TLE。下面是一种解题思路:
         我们注意到题目是查询第i个数字及以后有多少个互不相同的数字,那么突破点来了。

既然是求“以后”,如果我们从头往后遍历的话,那么难免两个查询的数区间的交叉部分是要重复遍历的。所以呢,我们就可以想到一种办法,就是将数组a从后往前遍历,并且声明一个相应大小的查询数组,每往前遍历一个数,便更新查询数组中相应的值。举个例子:

如果数组中有五个数,那么当从a[4]→a[3]的时候,这时候更新查询数组中ans[3]的值,并保存。那么这时候就还剩下一个问题尚待解决,题目要查询的是共有几个互不相同的数字。

这个问题其实很好解决,我们可以使用标记法,即另外再声明一个10^5量级的bool类型的数组。在输入数据的时候,每输入一个数据便将该bool类型的数组中的相应元素赋值为true.举个例子,就是说如果输入的是33,那么该bool类型的数组中的tmp[33]则赋值为true。表示输入的数据中含有33这个元素,所以呢在后面我们遍历的时候,已经遍历到的数字,便将该数对应的bool类型数组中相应元素赋值为false。这样的话便可以用一个if判断语句,判断当前遍历到的数字是否已经出现过,出现过便不再重复计数,这样的话就可以起到查询共有几个互不相同的数字的作用,而每当往前遍历的时候,便更新对应的查询数组的值。

         下面附上完整的代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int a[100005],ans[100005];   //全局变量初始值为0
bool tmp[100005];           //用来标记a[i]
int main()
{
    int n,q;
    while(scanf("%d %d",&n,&q) == 2 && n && q)
    {
        int sum = 0,ask;       //用来记录每次询问的结果,实时更新
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&a[i]);
            tmp[a[i]] = true;
        }
        for(int i = n - 1;i >= 0;i--)
        {
            if(tmp[a[i]])
            {
                sum++;
                tmp[a[i]] = false;    //表示该数已经出现过,往后不再对该数计数
            }
            ans[i] = sum;          //从后往前减少遍历次数
        }
        while(q--)
        {
            scanf("%d",&ask);
            printf("%d\n",ans[ask-1]);
        }
    }
    return 0;
}
如有错误,还请指正,O(∩_∩)O谢谢


http://www.niftyadmin.cn/n/1038255.html

相关文章

Vue路由动态控制

相信很多项目都会涉及到登录和权限&#xff0c;我个人写的也比较菜&#xff0c;希望大神们看到不要嘴下留情 先引入一下路由和vuex的依赖&#xff0c;对了我们这个写法是要用vuex配合的 npm install vue-router save npm install vuex --save之后我们在src下创建路由文件route…

预处理优化例题

题目描述&#xff1a; <span style"font-size:14px;">#include<stdio.h> typedef long long ll; ll f(ll a[],ll b[],ll len) {ll ans0,sum,i,j;for(i0;i<len;i){sum0;for(jb[i];j<i;j){suma[j];}anssum;}return ans; } ll a[500005],b[500005],le…

flex常用技巧

IT技术的使用往往因时代而变&#xff0c;也许昨天你还在吐槽flex可悲的兼容性&#xff0c;而今天你却发现小程序和uri-app跨平台开发对flex是那样友好 那么 对容器生命flex display: flex;flex默认是块级元素&#xff0c;如果希望声明为行内样式 display: inline-flex;align-…

离线处理例题

完整题目描述&#xff1a; <span style"font-size:14px;">#include<stdio.h> int a[100005][2]; int max(int a,int b){return a>b?a:b;} int main() {int n,q,i,x;while(scanf("%d",&n)!EOF)//n<100000{for(i0;i<n;i) scanf(&q…

js原生基础DOM操作

常用的dom操作如下 document.getElementById("id"); //通过ID名获取元素 document.getElementsByTagName("div"); //通过标签名获取元素 document.getElementsByClassName("class"); //通过类名获取元素 document.querySelector("se…

JavaScript生成范围随机数的方法

也许我们经常会遇到需要一个在一定范围内生成的随机数 例如 0 到 10之间参数以位随即谁&#xff0c;可js自带的功能里却无法实现这个需求 那么我们只好手动封装一个了 function random(n,m) {var comt m - n 1;return Math.floor(Math.random() * comt n) }然后我们调用方法…

C语言中 ln(以自然对数e为底) lg(以十为底) 以及logab(以a为底,b为真数)的相关知识

总所周知&#xff0c;我们在高中学过对数函数&#xff0c;记作ylogax。下面是百度百科关于对数函数的描述&#xff1a; 对数的定义&#xff1a;一般地&#xff0c;如果axN&#xff08;a>0&#xff0c;且a≠1&#xff09;&#xff0c;那么数x叫做以a为底N的对数&#xff0c;记…

React基础整理

前端一直流传着三大框架一大抄的说法&#xff0c;在中国的你或许无时不在感受这vue的强大&#xff0c;但其实react才是三大框架中世界使用了最大的 react官方地址 : http://react.html.cn/docs/getting-started.html 在这里我必须说明&#xff0c;react和vue谁更好是个争论不休…