BZOJ 1875: [SDOI2009]HH去散步
Time Limit: 20 Sec Memory Limit: 64 MB
Submit: 2244 Solved: 1144
[Submit][Status][Discuss]
Description
HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但
是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每
天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。 现在给你学校的地图(假设每条路的长度都
是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径
Input
第一行:五个整数N,M,t,A,B。
N表示学校里的路口的个数
M表示学校里的 路的条数
t表示HH想要散步的距离
A表示散步的出发点
B则表示散步的终点。
接下来M行
每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路。
数据保证Ai != Bi,但不保证任意两个路口之间至多只有一条路相连接。
路口编号从0到N -1。
同一行内所有数据均由一个空格隔开,行首行尾没有多余空格。没有多余空行。
答案模45989。
N ≤ 20,M ≤ 60,t ≤ 2^30,0 ≤ A,B
Output
一行,表示答案。
Sample Input
4 5 3 0 0
0 1
0 2
0 3
2 1
3 2
Sample Output
4
解题思路
树状数组+离线,如果将询问按照l排序,那么后面出现的与前面相同的可以替代前面的,所以记录一个nxt[i]数组,表示i这个位置往后第一个与i元素相同的元素的下标,然后将询问按照l排序,每次将此时扫到的元素的nxt在树状数组里+1。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1000005;
inline int rd(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int n,m,a[MAXN],nxt[MAXN],p[MAXN],mx,f[MAXN];
struct Data{
int l,r,id,ans;
}data[MAXN];
inline bool cmp(Data A,Data B){
if(A.l==B.l) return A.r<B.r;
return A.l<B.l;
}
inline bool cmp_(Data A,Data B){
return A.id<B.id;
}
inline void update(int x,int y){
for(;x<=n;x+=x&-x) f[x]+=y;
}
inline int query(int x){
int ret=0;
for(;x;x-=x&-x) ret+=f[x];
return ret;
}
int main(){
n=rd();
for(register int i=1;i<=n;i++){
a[i]=rd();
mx=max(a[i],mx);
}
for(register int i=n;i;i--) nxt[i]=p[a[i]],p[a[i]]=i;
for(register int i=1;i<=mx;i++) if(p[i]) update(p[i],1);
m=rd();
for(register int i=1;i<=m;i++)
data[i].l=rd(),data[i].r=rd(),data[i].id=i;
sort(data+1,data+1+m,cmp);
int l=1;
for(register int i=1;i<=m;i++){
while(l<data[i].l){
if(nxt[l]) update(nxt[l],1);
l++;
}
data[i].ans=query(data[i].r)-query(data[i].l-1);
}
sort(data+1,data+1+m,cmp_);
for(register int i=1;i<=m;i++)
printf("%d\n",data[i].ans);
return 0;
}