码迷,mamicode.com
首页 > 其他好文 > 详细

记一个问题的AC

时间:2014-10-05 22:56:29      阅读:313      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   io   os   ar   for   strong   

  今天突然做一道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)

 

  已经优化了一点点

  代码:  

bubuko.com,布布扣
#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!

 

bubuko.com,布布扣
#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

记一个问题的AC

标签:style   blog   http   color   io   os   ar   for   strong   

原文地址:http://www.cnblogs.com/keam37/p/4007399.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!