zoukankan      html  css  js  c++  java
  • 记一个问题的AC

      今天突然做一道LCT的染色问题的时候突然想到一个两个月前一道没有AC的题目。

      链接

      大意是,给一个长度为10^4的序列,最多有255个不同的数字,有最多10^5次方个询问,对于每个询问 l,r 输出[l,r]中不同数字的数目。

      记得最初的想法是  用f[i][j],存下数字i的第j次出现位置的下标 ,对每次询问,二分查找,判断每个数字i是否存在在[l,r]内的下标。

      时间复杂度是最多是 O((10^5)*255*log(10^4)) ,结果比赛的时候超时了,加了读入优化还是不行。

      由于比赛的OJ运行偏慢,1s的时限  比赛结束,只有3个人AC,最快的400ms,有一个刚刚好1000ms。

      于是我发了个帖子,询问答案,果然得到了更好的思路:

    1.

     离散化,C[i][j]::A[1]..A[i]中值为j的数的个数
     则数k在[L..R]中出现 <=> C[i][R]-C[i][L-1]>0.
      一次询问时间(255),时间复杂度是O(10^5*255)

      已经优化了一点点

      代码:  

    #include <cstdio>
    #include <cstdlib>
    #include <time.h>
    #include <iostream>
    using namespace std;
    const int INF = 100009;
    
    int f[INF][300],pos[INF];
    int g[INF];
    int n, m, tol;
    inline void rd (int &xx) {
        char ch = getchar();
        while (ch < '0' || ch > '9') ch = getchar();
        for (xx = 0; ch >= '0' && ch <= '9'; ch = getchar() )
            xx = xx * 10 + (ch - '0');
    }
    char o[35];
    inline void putnum (int x) {
        if (x == 0) {putchar ('0'); return;}
        int i;
        for (i = 0; x != 0; i++, x /= 10) o[i] = x % 10 + '0' ;
        for (i--; i >= 0; i--) putchar (o[i]);
        putchar (10);
    }
    int main() {
        rd (n); rd (m);
        for (int i = 1; i <= n; i++) {
            rd (g[i]);
            if (!pos[g[i]])    pos[g[i]] = ++tol;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= tol; j++)
                f[i][j] = f[i - 1][j] + (pos[g[i]] == j);
        }
        for (int i = 1, l, r; i <= m; i++) {
            int ans = 0;
            rd (l), rd (r);
            for (int j = 1; j <= tol; j++)
                if (f[r][j] - f[l - 1][j]) ans++;
            putnum (ans);
        }
        return 0;
    }
    View Code

     可是在加上了读入和输出优化和依旧超时!

     

    2.

    把所有询问存下来,并按照l由小到大的顺序依次回答。当左端点为t时,考虑一个数组b[],若b[i]=1则表示a[i]在a[t]到a[i-1]都没出现过,否则b[i]=0。
    那么任意一个左端点l=t,右端点r的询问都可以查询b[1]到b[r]的和得到答案。这个求区间和的操作可以用树状数组或线段树来解决。
    当回答完l=t的询问,要回答l=t+1的询问时,涉及到b[]数组的修改。发现只需要把b[t]由1变0,把b[right[t]]由0变1,其中a[t]右边第一个和它相等的数是a[right[t]],可以预处理出来。这两个修改对于树状数组来说太容易了,然后本题就做完了。
    补一句,如果题目要求强制在线,就把树状数组换成可持久化线段树,就搞定了。

    时间复杂度可以优化至 O(NlogN+Q)

    思路真是太棒了!看完这个我感觉这就是我想要的。

    于是再经过无数TLE,优化,TLE,再优化终于AC了,并且只用了234ms!

    #include <cstdio>
    #include <cstdlib>
    #include <time.h>
    #include <iostream>
    using namespace std;
    
    const int INF = 100009;
    
    int b[INF], ans[INF], qhead[INF], qr[INF], skip[INF];
    int g[INF], ne[INF], last[INF];
    int n, m, tol;
    inline void rd (int &xx) {
        char ch = getchar();
        while (ch < '0' || ch > '9') ch = getchar();
        for (xx = 0; ch >= '0' && ch <= '9'; ch = getchar() )
            xx = xx * 10 + (ch - '0');
    }
    char o[35];
    inline void putnum (int x) {
        if (x == 0) {putchar ('0'); return;}
        int i;
        for (i = 0; x != 0; i++, x /= 10) o[i] = x % 10 + '0' ;
        for (i--; i >= 0; i--) putchar (o[i]);
        putchar (10);
    }
    int main() {
        rd (n); rd (m);
        for (int i = 1; i <= n; i++) rd (g[i]);
        for (int i = 1, x; i <= m; i++) {
            rd (x), rd (qr[i]);
            skip[i] = qhead[x];
            qhead[x] = i;
        }
        for (int i = 1; i <= n; i++) {
            int t = g[i];
            if (last[t] == 0) for (int x = i; x <= n; x += x & -x) ++b[x];
            ne[last[t]] = i, last[t] = i;
        }
        for (int i = 1; i <= n; i++) {
            if (qhead[i])
                for (int t = qhead[i]; t; t = skip[t])
                    for (int x = qr[t]; x > 0; x -= x & -x) ans[t] += b[x];
    
            for (int x = i; x <= n; x += x & -x) --b[x];
            if (ne[i] != 0) for (int x = ne[i]; x <= n; x += x & -x) ++b[x];
        }
        for (int i = 1; i <= m; i++)
            putnum (ans[i]);
    
    }
    View Code

    感谢回答我问题的 nodgd 和  Hoblovski

  • 相关阅读:
    IntelliJ IDEA设置JDK1.8
    maven Create from archetype
    字符串的获取相关方法
    字符串比较
    题目:自定义4个学生对象,添加到集合,并遍历
    生成6个1-33之间的随机整数,添加到集合,并遍历集合。
    ArrayList的集合概述和基本使用
    对象数组
    匿名对象作为方法的参数和返回值
    构造方法
  • 原文地址:https://www.cnblogs.com/keam37/p/4007399.html
Copyright © 2011-2022 走看看