[USACO23FEB] Piling Papers G题解

洛谷[USACO23FEB] Piling Papers G

题目大意

给你一个长度为 n n n的序列 a a a,满足 1 ≤ a i ≤ 9 1\leq a_i\leq 9 1ai9,以及两个整数 A , B A,B A,B。有 q q q次询问,每次询问给出 l , r l,r l,r。对于每次询问,一开始有一个空串,对 a l , a l + 1 , … , a r a_l,a_{l+1},\dots,a_r al,al+1,,ar依次操作,对于每一个 a i a_i ai,你可以选择将 a i a_i ai放在 x x x的前面、后面、或者什么也不做。最后,将 x x x中的数字依次相连得到一个整数,求能使这个整数在 [ A , B ] [A,B] [A,B]之间的方案数的数量,输出对 1 0 9 + 7 10^9+7 109+7取模。

1 ≤ n ≤ 300 , 1 ≤ A , B ≤ 1 0 18 1\leq n\leq 300,1\leq A,B\leq 10^{18} 1n300,1A,B1018


题解

用差分将要求的部分变为 [ 0 , B ] [0,B] [0,B]的方案数减去 [ 0 , A − 1 ] [0,A-1] [0,A1]的方案数,那么我们只需考虑求 [ 0 , k ] [0,k] [0,k]的方案数。

f i , l , r , 0 / 1 / 2 f_{i,l,r,0/1/2} fi,l,r,0/1/2表示前 i i i个数形成的 r − l + 1 r-l+1 rl+1个数字组成的数,和 k k k从高到低的第 l l l位到第 r r r位组成的数的关系是小于、等于还是大于。转移就是从 f i , l + 1 , r , 0 / 1 / 2 f_{i,l+1,r,0/1/2} fi,l+1,r,0/1/2 f i , l , r − 1 , 0 / 1 / 2 f_{i,l,r-1,0/1/2} fi,l,r1,0/1/2转移到 f i , l , r , 0 / 1 / 2 f_{i,l,r,0/1/2} fi,l,r,0/1/2

k k k的位数为 d d d,那么对于序列 a a a 1 1 1 i i i的答案 s u m i = f n , 1 , d , 0 + f n , 1 , d , 1 + ∑ j = 1 d − 1 ∑ p = 0 2 f n , 1 , j , p sum_i=f_{n,1,d,0}+f_{n,1,d,1}+\sum\limits_{j=1}^{d-1}\sum\limits_{p=0}^2f_{n,1,j,p} sumi=fn,1,d,0+fn,1,d,1+j=1d1p=02fn,1,j,p。前两项求的是位数等于 d d d且值小于等于 k k k的方案数,最后一项求的是位数小于 d d d的方案数。

a n s l i , j ansl_{i,j} ansli,j表示当 k = A − 1 k=A-1 k=A1 a i , a i + 1 … , a j a_{i},a_{i+1}\dots,a_{j} ai,ai+1,aj的答案,则 a n s l i , j = a u m j − s u m i − 1 ansl_{i,j}=aum_j-sum_{i-1} ansli,j=aumjsumi1。但实际上,这样算还麻烦了,我们其实并不需要求 s u m i sum_i sumi。对于同一个 i i i,在枚举 j j j时继续用先前的 f f f数组,就能 O ( n 2 log ⁡ 2 A ) O(n^2\log^2 A) O(n2log2A)预处理出 a n s l i , j ansl_{i,j} ansli,j
a n s r i , j ansr_{i,j} ansri,j表示当 k = B k=B k=B a i , a i + 1 … , a j a_{i},a_{i+1}\dots,a_{j} ai,ai+1,aj的答案,则同理可以 O ( n 2 log ⁡ 2 B ) O(n^2\log^2B) O(n2log2B)预处理出 a n s r i , j ansr_{i,j} ansri,j。这样的话,查询就是 O ( 1 ) O(1) O(1)的了。

总时间复杂度为 O ( n 2 log ⁡ 2 B + q ) O(n^2\log^2B+q) O(n2log2B+q)

#include<bits/stdc++.h>
using namespace std;
const long long mod=1000000007;
int n,q,v1,a[305],v[25];
long long ansl[305][305],ansr[305][305],f[25][25][3];
long long L,R;
void dd(long long x){
	v1=0;
	while(x){
		v[++v1]=x%10;x/=10;
	}
	reverse(v+1,v+v1+1);
}
int gt(int x,int y){
	if(x<y) return 0;
	else if(x==y) return 1;
	else return 2;
}
int main()
{
	scanf("%d%lld%lld",&n,&L,&R);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	dd(L-1);
	for(int i=1;i<=n;i++){
		memset(f,0,sizeof(f));
		for(int j=i;j<=n;j++){
			for(int l=1;l<=v1;l++){
				for(int r=v1;r>l;r--){
					if(a[j]>v[l]){
						for(int k=0;k<=2;k++)
						f[l][r][2]=(f[l][r][2]+f[l+1][r][k])%mod;
					}
					else if(a[j]==v[l]){
						for(int k=0;k<=2;k++)
						f[l][r][k]=(f[l][r][k]+f[l+1][r][k])%mod;
					}
					else{
						for(int k=0;k<=2;k++)
						f[l][r][0]=(f[l][r][0]+f[l+1][r][k])%mod;
					}
					f[l][r][2]=(f[l][r][2]+f[l][r-1][2])%mod;
					f[l][r][gt(a[j],v[r])]=(f[l][r][gt(a[j],v[r])]+f[l][r-1][1])%mod;
					f[l][r][0]=(f[l][r][0]+f[l][r-1][0])%mod;
				}
			}
			for(int p=1;p<=v1;p++) f[p][p][gt(a[j],v[p])]=(f[p][p][gt(a[j],v[p])]+2)%mod;
			ansl[i][j]=(ansl[i][j]+f[1][v1][0]+f[1][v1][1])%mod;
			for(int p=1;p<v1;p++){
				for(int k=0;k<=2;k++)
				ansl[i][j]=(ansl[i][j]+f[1][p][k])%mod;
			}
		}
	}
	dd(R);
	for(int i=1;i<=n;i++){
		memset(f,0,sizeof(f));
		for(int j=i;j<=n;j++){
			for(int l=1;l<=v1;l++){
				for(int r=v1;r>l;r--){
					if(a[j]>v[l]){
						for(int k=0;k<=2;k++)
						f[l][r][2]=(f[l][r][2]+f[l+1][r][k])%mod;
					}
					else if(a[j]==v[l]){
						for(int k=0;k<=2;k++)
						f[l][r][k]=(f[l][r][k]+f[l+1][r][k])%mod;
					}
					else{
						for(int k=0;k<=2;k++)
						f[l][r][0]=(f[l][r][0]+f[l+1][r][k])%mod;
					}
					f[l][r][2]=(f[l][r][2]+f[l][r-1][2])%mod;
					f[l][r][gt(a[j],v[r])]=(f[l][r][gt(a[j],v[r])]+f[l][r-1][1])%mod;
					f[l][r][0]=(f[l][r][0]+f[l][r-1][0])%mod;
				}
			}
			for(int p=1;p<=v1;p++) f[p][p][gt(a[j],v[p])]=(f[p][p][gt(a[j],v[p])]+2)%mod;
			ansr[i][j]=(ansr[i][j]+f[1][v1][0]+f[1][v1][1])%mod;
			for(int p=1;p<v1;p++){
				for(int k=0;k<=2;k++)
				ansr[i][j]=(ansr[i][j]+f[1][p][k])%mod;
			}
		}
	}
	scanf("%d",&q);
	while(q--){
		int l,r;
		scanf("%d%d",&l,&r);
		printf("%lld\n",(ansr[l][r]-ansl[l][r]+mod)%mod);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值