范围查询所需的高效算法

时间:2012-09-27 02:47:38

标签: c++ algorithm

我想知道实现这个(方法)的最佳方法。我将给出一组坐标(x,y)。然后我会根据这些坐标来查询
1.C a b =>其中a和b是初始坐标集中的整数索引。所以我需要输出第一,第二,第三和第四象限中索引范围为a到b的点数。


2.X a b =>其中a和b是初始坐标集中的整数索引。所以我需要将ath镜像到沿x轴的索引坐标。


3.Y a b =>其中a和b是初始坐标集中的整数索引。所以我需要将ath镜像到沿y轴的索引坐标。

最多可以有100000个坐标或点,以及500000个这样的查询。

我尝试了在每个查询中循环的暴力方法,但它有TLE(超出时间限制)。

在这类问题中我该怎么做?

这是我的代码

#include <iostream>
#include <stdio.h>

using namespace std;

char flipX[4] = { 3, 2, 1, 0 };
char flipY[4] = { 1, 0, 3, 2 };

int main(int argc, char **argv)
{
    int n,x,y;
    char c[100000];
    scanf("%d",&n);
    //coord *c=new coord[n];
    for(int i=0;i<n;i++)
    {
        scanf("%d",&x);
        scanf("%d",&y);
        if(x<0 && y<0)
            c[i]=2;
        else if(x>0 && y>0)
            c[i]=0;
        else if(x>0 && y<0)
            c[i]=3;
        else
            c[i]=1;
    }

    int q,i,j,a,cnt[4];
    char ch;
    scanf("%d",&q);
    while(q--)
    {
        //cout<<"q:"<<q<<endl;
        cin>>ch;
        scanf("%d",&i);
        scanf("%d",&j);
        i--;j--;
        if(ch=='X')
        {
            //case 'X':
                for(a=i;a<=j;a++)
                    c[a]=flipX[c[a]];
            //  break;
        }
        else if(ch=='Y')
        {
            //case 'Y':
                for(a=i;a<=j;a++)
                    c[a]=flipY[c[a]];
            //  break;
        }   
        else if(ch=='C')
        {
                cnt[0]=cnt[1]=cnt[2]=cnt[3]=0;
                for(a=i;a<=j;a++)
                {
                    cnt[c[a]]++;
                }
                printf("%d %d %d %d\n",cnt[0],cnt[1],cnt[2],cnt[3]);
        }
    }
    return 0;
}

请帮助。

2 个答案:

答案 0 :(得分:1)

同意nneonneo。

const size_t MAX_COORDS = 100000;
vector<char> quadrant( MAX_COORDS );

此处,quadrant维护一个值(0到3)。在没有任何条件的情况下翻转象限是非常简单的:

char flipX[4] = { 2, 3, 0, 1 };
char flipY[4] = { 3, 2, 1, 0 };

vector<char>::iterator itLeft = quadrant.begin() + left;
vector<char>::iterator itRight = quadrant.begin() + right + 1;

for( vector<char>::iterator it = itLeft; it != itRight; it++ )
{
    *it = flipX[*it];
}

计算象限同样容易:

unsigned int count[4] = {0};
for( vector<char>::iterator it = itLeft; it != itRight; it++ )
{
    count[*it]++;
}

如果你需要比这更快,你将不得不采取动态编程方法并记住每个点和每个象限的象限计数。这将为您提供O(1)范围搜索,但会使镜像操作更加昂贵。以下是范围计数的工作原理:

vector< vector<char> > counts( 4, vector<char>(MAX_COORDS) );

// ...

for( size_t i = 0; i < 4; i++ ) {
    count[i] = counts[i][right] - (left > 0 ? counts[i][left-1] : 0);
}

答案 1 :(得分:0)

确定。懒惰的树木成功了。谢谢大家的帮助