BZOJ 4403 序列统计 题解

题面

传送门

分析

我们首先考虑长度为 l l ,元素大小在 L R R 的==单调不降序列==的个数。设元素大小为 L R R 的元素的个数分别为 x1,x2,x3,,xRL+1 则题目转化为求方程 RL+1i=1xi=l ∑ i = 1 R − L + 1 x i = l 的非负整数解的个数。

yi=xi+1 y i = x i + 1 ,则转化为求方程 RL+1i=1yi=l+RL+1 ∑ i = 1 R − L + 1 y i = l + R − L + 1 的正整数解得个数。

len=RL+1 l e n = R − L + 1 ,隔板法,得到答案:

(l+len1len1) ( l + l e n − 1 l e n − 1 )

题目要求的是长度为 1 1 N ,元素大小在 L L R 的单调不降序列的个数。设答案为 Ans A n s ,则
Ans=(1+len1len1)+(2+len1len1)+(3+len1len1)++(N+len1len1) A n s = ( 1 + l e n − 1 l e n − 1 ) + ( 2 + l e n − 1 l e n − 1 ) + ( 3 + l e n − 1 l e n − 1 ) + ⋯ + ( N + l e n − 1 l e n − 1 )

等号两边同时加上 (lenlen) ( l e n l e n ) ,得到
Ans+(lenlen)=(lenlen)+(1+len1len1)+(2+len1len1)++(N+len1len1) A n s + ( l e n l e n ) = ( l e n l e n ) + ( 1 + l e n − 1 l e n − 1 ) + ( 2 + l e n − 1 l e n − 1 ) + ⋯ + ( N + l e n − 1 l e n − 1 )

化简, 得
Ans=(N+lenlen)1 A n s = ( N + l e n l e n ) − 1

到现在为止思路已经讲完了。然而 1N,L,R109 1 ≤ N , L , R ≤ 10 9 ,阶乘直接上天。

Lucas定理

对于非负整数m和n和素数p, 同余式:

(mn)i=0k(mini)(modp), ( m n ) ≡ ∏ i = 0 k ( m i n i ) ( mod p ) ,

成立,其中:
m=mkpk+mk1pk1++m1p+m0, m = m k p k + m k − 1 p k − 1 + ⋯ + m 1 p + m 0 ,

并且
n=nkpk+nk1pk1++n1p+n0 n = n k p k + n k − 1 p k − 1 + ⋯ + n 1 p + n 0

是m和n的p进制展开。 当 m<n m < n 时,二项式系数 (mn)=0 ( m n ) = 0

由于结果要对 106+3 10 6 + 3 取模,我们只需要将 N+len N + l e n len l e n 转化为 106+3 10 6 + 3 进制,再将他们对应的每一位用一遍组合公式。因此我们需要预处理出 0 0 106+2 的阶乘和它们在模 106+2 10 6 + 2 意义下的逆。阶乘可以从0递推得到,然后使用费马小定理,用快速幂计算出 106+2 10 6 + 2 的逆,然后反着推回去。

代码实现

#include <cstdio>

typedef long long ll;
const int ha=1000003;

int ksm(int x, int a) {
    int ret=1;
    for(; a; ret=(a%2)?(ll)ret*x%ha:ret, x=(ll)x*x%ha, a/=2);
    return ret;
}

int jc[ha], ni[ha];

void init() {
    jc[0]=1;
    for(int i=1; i<ha; i++) jc[i]=(ll)jc[i-1]*i%ha;
    ni[ha-1] = ksm(jc[ha-1], ha-2);
    for(int i=ha-1; i; i--) ni[i-1]=(ll)ni[i]*i%ha;
}

int c(int n, int m) {
    if(n<m) return 0;
    return (ll)jc[n]*ni[m]%ha*ni[n-m]%ha;
}

int C(int n, int m) {
    int ret=1;
    for(; n&&ret; ret=(ll)ret*c(n%ha,m%ha)%ha, n/=ha, m/=ha);
    return ret;
}

int T, n, L, R;

int main() {
    scanf("%d", &T);
    init();
    while(T--) {
        scanf("%d%d%d", &n, &L, &R);
        int len=R-L+1;
        printf("%d\n", (C(n+len, len)-1+ha)%ha);
    }
    return 0;
} 
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值