Algorithm SPOJ DQUERY:TLE即使有位?

Algorithm SPOJ DQUERY:TLE即使有位?,algorithm,data-structures,fenwick-tree,Algorithm,Data Structures,Fenwick Tree,这里是我想要解决的问题,我使用前缀和[i]-前缀和[i-1]导致频率大于零的事实来识别不同的数字,然后我消除频率,但即使使用位,我也会得到一个TLE 给定一个由n个数字a1、a2、…、an和若干个d查询组成的序列 d查询是一对i,j 1≤ 我≤ J≤ n 对于每个d查询i,j,必须返回子序列ai,ai+1,…,aj中不同元素的数量 Input Line 1: n (1 ≤ n ≤ 30000). Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 106).

这里是我想要解决的问题,我使用前缀和[i]-前缀和[i-1]导致频率大于零的事实来识别不同的数字,然后我消除频率,但即使使用位,我也会得到一个TLE

给定一个由n个数字a1、a2、…、an和若干个d查询组成的序列

d查询是一对i,j 1≤ 我≤ J≤ n

对于每个d查询i,j,必须返回子序列ai,ai+1,…,aj中不同元素的数量

Input

Line 1: n (1 ≤ n ≤ 30000).
Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 106).
Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
In the next q lines, each line contains 2 numbers i, j 
representing a d-query (1 ≤ i ≤ j ≤ n).

Output

For each d-query (i, j), print the number of distinct elements in the 
subsequence ai, ai+1, ..., aj in a single line.
Example

Input
5 
1 1 2 1 3
3
1 5
2 4
3 5

Output
3
2
3
代码是:

#include <iostream>
#include <algorithm>
#include <vector>
#include <stdlib.h>
#include <stdio.h>
typedef long long int ll;
using namespace std;
void update(ll n, ll val, vector<ll> &b);
ll read(ll n,vector<ll> &b);
ll readsingle(ll n,vector<ll> &b);
void map(vector<ll> &a,vector<ll> &b,ll n)  /**** RElative Mapping ***/
{
    ll temp;
    a.clear();
    b.clear();
    for(ll i=0; i<n; i++)
    {
        cin>>temp;
        a.push_back(temp);
        b.push_back(temp);
    }
    sort(b.begin(),b.end());
    for(ll i=0; i<n; i++)
        *(a.begin()+i) = (lower_bound(b.begin(),b.end(),a[i])-b.begin())+1;
    b.assign(n+1,0);
}
int main()
{
    ll n;
    cin>>n;
    vector<ll> a,b;
    map(a,b,n);
    ll t;
    cin>>t;
    while(t--)
    {
        ll l ,u;
        b.assign(n+1,0);
        cin>>l>>u;
        l--;/*** Reduce For Zero Based INdex ****/
        u--;
        for(ll i=l;i<=u;i++)
            update(a[i],1,b);
        ll cont=0;
        for(ll i=l;i<=u;i++)
            if(readsingle(a[i],b)>0)
        {
            cont++;
            update(a[i],-readsingle(a[i],b),b); /***Eliminate The Frequency */
        }
        cout<<cont<<endl;
    }
    return 0;
}
ll readsingle(ll n,vector<ll> &b)
{
    return read(n,b)-read(n-1,b);
}
ll read(ll n,vector<ll> &b)
{
    ll sum=0;
    for(; n; sum+=b[n],n-=n&-n);
    return sum;
}
void update(ll n, ll val, vector<ll> &b)
{
    for(; n<=b.size(); b[n]+=val,n+=n&-n);
}

你使用的算法太慢了。对于每个查询,您都要在整个查询范围内进行迭代,这已经给出了n*q操作。显然,这太多了。这是一个更好的解决方案它具有On+q*log n时间和On+q空间复杂性它是一个离线解决方案:

让我们按查询的右端对所有查询进行排序。不需要显式排序,只需将查询添加到从0到n-1的适当位置即可

现在让我们从左到右遍历数组中的所有位置,并保持一点。位中的每个位置都是1,这意味着在位置i或0处有一个新元素。最初,它用零填充

对于每个元素a[i]:如果它是该元素的第一次出现,只需在位中的i位置添加一个。否则,将-1添加到该元素上一次出现的位置,然后将1添加到i位置

查询left,right的答案是从左到右的所有元素的总和

要保持每个元素的最后一次出现,可以使用贴图


可以使用持久段树使其在线。时间复杂度将是相同的,相同的复杂度将在*logn+q上变为相同的复杂度,但此处不需要

对于每个查询,你运行一个1到u和1到l的循环,所以每个查询取Ou*logn,u可以大到n,所以对于q查询Oq*n*logn,它相当高。对于每个查询,我运行循环u-l+1*2次,这只是查询中范围的长度。我无法理解为什么在代码中使用位?在您的算法中,一个数组就足够了,您仍然可以扫描整个段。那么@PhamTrung,在这个问题中您会使用什么?因为数组中的元素仅限于0到10^6,所以使用数组代替映射就足够了,但是这个解决方案很好,谢谢:请您解释一下数组={3,1,3}和查询为1,1的过程,1基indexed@migdal我们从左到右迭代数组。第一个元素是3。这是该元素的第一次出现,因此我们将1添加到位置1。然后我们回答这个问题:从1到1的和是1。之后,我们继续迭代数组,但这并不重要,因为没有更多的查询。理解按末尾排序的间隔非常有帮助,好主意,谢谢alot@WasimThabraze我们可以在遍历数组时维护一个map元素->到目前为止的最后一次出现。在这种情况下,查找最后一个事件是O1或Olog N。