[cogs2692]天才魔法少女琪露诺爱计数

题目描述】

琪露诺闲来无事,用冰块制造了了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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值