题目描述】
琪露诺闲来无事,用冰块制造了了N个台阶,沿直线依次摆放,编号1, 2, 3...,⑨, ...N,分别高H(1), H(2), ... H(N)
琪露诺很满意自己的作品,想把自己抓到的青蛙放到台阶上玩,玩法也很简单。 青蛙最开始在第一个台阶上,它只能往前面编号更大的台阶上跳。如果现在在第k个台阶上,它至少要跳到第(k+L)个台阶上,最多只能跳到第(k+R)个台阶上,而且目标台阶现在所在的这个台阶,它们高度的绝对值之差,不能超过T。否则青蛙会跳不上去或者摔死。(这时候只有琪露诺给它续它才能复活,而琪露诺是不会给青蛙续的)。
琪露诺想知道青蛙跳到第N个,也就是最后一个台阶上,有多少种路线呢?琪露诺是天才魔法少女,但是数数用的是⑨进制,她数出来的结果别人是无法理解的,所以她找你来帮忙了,请你告诉大家,青蛙有有多少条路线可以走呢?告诉大家方案数对998244353取模的结果就可以啦,无法到达的话,方案数自然就是0。
【输入格式】
第一行4个正整数,N, L, R, T,含义如题面所示
接下来第二行,N个正整数,依次表示这N个高台的高度。
【输出格式】
青蛙跳到最后一个高台上面的方案数对998244353取模的结果。
【样例】
input1
3 2 2 0
1 100 1
output1
1
解释1
只有一种方案,从第1个高台直接跳到第3个高台。第二个高台虽然高100但是并不会影响青蛙跳过它,因为这些青蛙是经过早苗开光的
input2
5 1 2 5
1 2 3 4 5
output2
5
解释2:
#5条路线分别是(数字代表高台的编号):
1-2-3-4-5
1-2-3-5
1-2-4-5
1-3-4-5
1-3-5
input3
20 1 4 12
1 2 3 4 5 6 7 100 9 10 11 12 11 100 1 2 3 4 5 6
output3
27680
解释3
良心大♂样例
【数据范围】
对于20%的数据:N <= 20,
对于另外20%的数据:N <= 10000, R-L+1 <= 1000
对于另外20%的数据:N <= 20000,T = 0
对于另外20%的数据:N <= 20000。
对于100%的数据:N <= 100000, 1 <= R-L+1 <= N,1 <= H(i) <= 10000, T <= 10000
【来源】
by sxysxy。原题地址: http://syzoj.com/problem/314
题解:
计数题 但状态转移时间复杂度太高 就必须要用一种数据结构来维护
方程:f[i]=sigma(j,max(1,i-R) to i-L)且abs(H[i]-H[j]<=T)f[j](i>L)
很容易看出 一维数据结构依然满足不了高效 可以采用二维数据结构
我们维护n棵线段树,每棵线段树下标为高度,对于第i棵线段树,维护第1-i个冰块的方案数之和,那么f[i]变为在第i-L棵线段树与第max(1,i-R)棵线段树的差线段树上查询[H[i]-T,H[i]+T]这个区间权值和
n棵线段树空间会爆,要用可持久化来减少空间损失。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=100000+10;
const long long MOD=998244353;
long long f[maxn]={0};
int root[maxn*50],ls[maxn*50],rs[maxn*50];
long long summ[maxn*50];
int sz=0;
int h[maxn];
int n,m;
inline void insert(int last,int l,int r,int &rt,int x,long long v){
rt=++sz;
summ[rt]=((summ[last]+v)%MOD+MOD)%MOD;
if(l==r)
return ;
int mid=(l+r)>>1;
ls[rt]=ls[last],rs[rt]=rs[last];
if(mid>=x)
insert(ls[last],l,mid,ls[rt],x,v);
else insert(rs[last],mid+1,r,rs[rt],x,v);
}
inline long long query(int L,int R,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)
return summ[R]-summ[L];
if(l==r)
return 0;
int mid=(l+r)>>1;
long long ans=0;
if(mid>=ql)
ans+=query(ls[L],ls[R],l,mid,ql,qr);
ans%=MOD;
if(mid<qr)
ans+=query(rs[L],rs[R],mid+1,r,ql,qr);
ans%=MOD;
return ans;
}
int main(){
freopen("cirnoisclever.in","r",stdin);
freopen("cirnoisclever.out","w",stdout);
int L,R;
scanf("%d %d %d %d",&n,&L,&R,&m);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
f[1]=1;
insert(root[0],1,10000,root[1],h[1],f[1]);
for(int i=2;i<=n;i++){
int l=max(1,i-R),r=i-L;
if(r>=1){
long long T=query(root[l-1],root[r],1,10000,max(h[i]-m,1),min(h[i]+m,10000))%MOD;
f[i]=(T%MOD+MOD)%MOD;
}
insert(root[i-1],1,10000,root[i],h[i],f[i]);
}
printf("%lld\n",f[n]);
return 0;
}
青蛙跳跃问题

解决一个关于青蛙跳跃路径计数的问题,通过使用线段树和可持久化数据结构优化算法。
354





