您的位置:首页 > 其它

【HZOI】 赏花

2013-10-04 19:18 148 查看
Description
衡水湖为了迎接国际马拉松比赛,在跑道旁种了一排花,每朵花都有一种颜色色,共有N 朵。 

现在 sky 和 leaf 到衡水湖赏花,leaf 总会问 sky 一些问题,比如第l朵花到第r朵花之间能看到多少种颜色的花? 

sky 算得木有那么快,于是请你来帮帮他。
Input
第一行一个数N 

接下来一行N 个数Ci,表示花的颜色 

接下来一行一个数M 

接下来M行,每行两个数l,r,表示 leaf 的一个问题,询问l到r之间有多少种颜色的花。
Output
M行,每行表示 leaf 一个问题的答案
Sample Input

6
1 2 3 4 3 5
3
1 2
3 5
2 6


Sample Output

2
2
4


Hint
N≤100000,M≤200000,1≤Ci≤100000

【分析】

        看完题目,第一反应是尽管提问次数很多,但没有修改操作。于是便可以离线处理了。离线处理也就意味着预处理,那空间换时间。那么怎么换呢?可以用树状数组来维护一个前缀和。具体步骤如下:

      1.给提问按照左区间端点为第一关键字,右区间端点为第二关键字(右区间排序可有可无),均从小到大排序。

      2.从后往前所有相同的颜色拉成一个链表(像存边一样,一个last数组和一个next数组)。然后将所有的last即每个颜色从左边第一次出现的位置置为1(同时树状数组也修改)。

      3.查询区间时,将当前区间左端点左边的所有的1全部移至相应的next位置。原位置改为0,next位置改为1。那么这段区间的答案便是get(y)-get(x-1)。get表示利用树状数组求前缀和。

    再看看这种做法,其实质也就相当于先让每种颜色只出现在最左边的位置。然后随着区间的移动(排序后的),颜色依次出现在下一个位置。然后原位置标记为空,来保证一种颜色不会重复计算。而树状数组只不过是维护动态的前缀和罢了。

(PS:注意输出答案的时候,把顺序调整回来)

【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<iostream>
#include<algorithm>
#define _lowbit(x) (x&(-x))
using namespace std;
int N,M,v[100005],qx[200005],qy[200005],rank[200005],ans[200005];
int c[100005],last[100005],next[100005];
int maxv=0;
void _qst_rank(int l,int r)
{
int i=l,j=r,mr=rank[(i+j)>>1];
while(i<=j)
{
while(rank[i]<mr) i++;
while(rank[j]>mr) j--;
if(i<=j)
{
swap(rank[i],rank[j]);
swap(ans[i],ans[j]);
i++;j--;
}
}
if(l<j) _qst_rank(l,j);
if(i<r) _qst_rank(i,r);
}
void _qst_qxqy(int l,int r)
{
int i=l,j=r,mx=qx[(i+j)>>1],my=qy[(i+j)>>1];
while(i<=j)
{
while((qx[i]<mx)||(qx[i]==mx&&qy[i]<my)) i++;
while((qx[j]>mx)||(qx[j]==mx&&qy[j]>my)) j--;
if(i<=j)
{
swap(qx[i],qx[j]);
swap(qy[i],qy[j]);
swap(rank[i],rank[j]);
i++;j--;
}
}
if(l<j) _qst_qxqy(l,j);
if(i<r) _qst_qxqy(i,r);
}
void _in(int &x)
{
char t=getchar();
while(t<'0'||'9'<t) t=getchar();
for(x=t-'0',t=getchar();'0'<=t&&t<='9';x=x*10+t-'0',t=getchar());
}
void _out(int x,int j=1)
{
char t[100];
if(x==0) putchar('0');
for(;x;x/=10,j++) t[j]=x%10+'0';
for(j--;j;j--) putchar(t[j]);
putchar('\n');
}
void _init()
{
_in(N);
for(int i=1;i<=N;i++)
{
_in(v[i]);
maxv=max(maxv,v[i]);
}
_in(M);
for(int i=1;i<=M;i++)
{
_in(qx[i]);
_in(qy[i]);
rank[i]=i;
}
}
void _solve()
{
for(int i=N;i;i--)    //逆序建立链表
{
next[i]=last[v[i]];
last[v[i]]=i;
}
_qst_qxqy(1,M);     //将问题排序
for(int i=1;i<=maxv;i++)
if(last[i]!=0)
for(int j=last[i];j<=N;j+=_lowbit(j))
c[j]++;
for(int i=1;i<=M;i++)
{
for(int k=max(1,qx[i-1]);k<qx[i];k++)
{
for(int j=k;j<=N;j+=_lowbit(j))   //删除之前的标记
c[j]--;
if(next[k]!=0)    //如果可以移动,则移至下一位置
for(int j=next[k];j<=N;j+=_lowbit(j))
c[j]++;
}
int temp=0;     //计算答案
for(int j=qx[i]-1;j;j-=_lowbit(j))
temp-=c[j];
for(int j=qy[i];j;j-=_lowbit(j))
temp+=c[j];
ans[i]=temp;
}
_qst_rank(1,M);      //按输入顺序排序
for(int i=1;i<=M;i++)
_out(ans[i]);
}
int main()
{
_init();
_solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: