6476. 【GDOI2020模拟02.19】A

题目

题目比较简洁,就不复述了。


思考历程

这让我联想到了不久之前在CF上做的一道题。
但这两道题的差别是很大的,共同点并不是很多。
直接套那题的方法在这题中也顶多拿个40分。
考虑探索新大陆,然而没有成功……
感觉这题的暴力不好打,最终也没有打部分分……
(所以说我在这题上耗费了1h)


正解

正解的思路比较清奇。
不用说也知道这题要容斥一下,我比赛时一直在想着保留上界,然而正解是保留下界。
如果想到的话也挺显然的,首先用最终的值减去下界,接下来用生成函数去考虑,就相当于\(\frac{1}{(1-x)^n}\)。这个东西可以用隔板法搞出来。假如最终的总和为\(V\),下界和为\(S\),那么方案数就是\(C_{V-S+n-1}^{n-1}\)
我们要将每个合法的\(V\)的这个东西加起来。
有条公式:\(C_{n+m}^k=\sum_{i=0}^kC_n^iC_m^{k-i}\)
可以用组合意义证明。我们用这条式子来搞事情。
假如知道了\(V\)的信息,现在要知道\(V+aD^b\)的信息
\(C_{V+aD^b-S+n-1}^{n-1}=\sum_{i=0}^{n-1}C_{V-S+n-1}^iC_{aD^b}^{n-1-i}\)
于是,只要我们知道\(C_{V-S+n-1}^i\)(\(i \in [0,n)\)),就可以推出\(V+aD^b\)的信息了。右边的那个东西预处理即可。
可以发现不同的\(V\)是可以加在一起转移的。
发现了这个东西之后就可以DP:设\(f_{i,j,0/1}\),表示前\(i\)位,计算的是\(C_{V-S+n-1}^{j}\)的和,后面的\(0/1\)表示前\(i\)位的\(V\)和下界总和(即\(S\))的大小关系。


代码

代码中\(f\)数组加了一维,表示有没有前导零。

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define mo 1000000007
#define LEN 520
#define maxD 500
#define N 12
#define ll long long
inline ll qpow(ll x,int y=mo-2){
    ll res=1;
    for (;y;y>>=1,x=x*x%mo)
        if (y&1)
            res=res*x%mo;
    return res;
}
int n,D;
ll fac[N],ifac[N];
inline ll C(ll m,int n){
    m%=mo,m+=(m<0?mo:0);
    ll res=ifac[n];
    for (int i=0;i<n;++i,m=(m-1+mo)%mo)
        res=res*m%mo;
    return res;
}
ll powd[LEN+1],g[LEN+1][maxD][N],sg[LEN+1][maxD][N];
struct Bigint{
    int k,v[LEN+1];
    inline int getmo(){
        int res=0;
        for (int i=k;i>=0;--i)
            res=((ll)res*D+v[i])%mo;
        return res;
    }
} L[N],R[N];
void operator*=(Bigint &a,int b){
    for (int i=0;i<=a.k;++i)
        a.v[i]*=b;
    for (int i=0;i<=a.k;++i){
        a.v[i+1]+=a.v[i]/D;
        a.v[i]%=D;
    }
    if (a.v[a.k+1])
        a.k++;
}
void operator+=(Bigint &a,int b){
    a.v[0]+=b;
    for (int i=0;a.v[i]>=D;++i)
        a.v[i+1]++,a.v[i]-=D;
    if (a.v[a.k+1])
        a.k++;
}
void operator+=(Bigint &a,Bigint &b){
    a.k=max(a.k,b.k);
    for (int i=0;i<=a.k;++i){
        a.v[i]+=b.v[i];
        if (a.v[i]>=D)
            a.v[i+1]++,a.v[i]-=D;
    }
    if (a.v[a.k+1])
        a.k++;
}
void operator-=(Bigint &a,Bigint &b){
    for (int i=0;i<=a.k;++i){
        a.v[i]-=b.v[i];
        if (a.v[i]<0)
            a.v[i+1]--,a.v[i]+=D;
    }
    if (a.v[a.k]==0)
        a.k--;
}
void input(Bigint &a){
    char ch=getchar();
    while (ch<'0' || ch>'9')
        ch=getchar();
    do{
        a*=10,a+=ch-'0';
        ch=getchar();
    }
    while ('0'<=ch && ch<='9');
}
bool T[maxD];
ll ans;
ll f[LEN+1][N][2][2];
Bigint s;
inline ll calc(){
    memset(f[0],0,sizeof f[0]);
    int smo=s.getmo();
    for (int j=0,w=s.v[0];j<n;++j){
        for (ll v=0;v<D;++v)
            if (T[v]){
                ll tmp=C(v-smo+n-1,j);
                (f[0][j][v>=w][bool(v)]+=tmp)%=mo;
            }
    }
    for (int i=0;i<LEN;++i){
        int w=s.v[i+1];
        memset(f[i+1],0,sizeof f[i+1]);
        for (int j=0;j<n;++j)
            for (int c=0;c<2;++c){
                ll val=(f[i][j][c][1]+(T[0]?f[i][j][c][0]:0))%mo;
                for (int k=j;k<n;++k){
                    if (w)
                        (f[i+1][k][0][1]+=val*sg[i+1][w-1][k-j])%=mo;
                    if (w && T[w])
                        (f[i+1][k][c][1]+=val*g[i+1][w][k-j])%=mo;
                    (f[i+1][k][1][1]+=val*(sg[i+1][D-1][k-j]-sg[i+1][w][k-j]+mo))%=mo;
                }
                (f[i+1][j][0<w?0:c][0]+=f[i][j][c][1]+f[i][j][c][0])%=mo;
            }
    }
    ll res=f[LEN][n-1][1][1]+f[LEN][n-1][1][0];
    return res%mo;
}
void dfs(int k,int flag){
    if (k==n){
        ans+=calc()*flag;
        return;
    }
    s+=L[k];
    dfs(k+1,flag);
    s-=L[k];
    s+=R[k];
    dfs(k+1,-flag);
    s-=R[k];
}
int main(){
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout);
    scanf("%d%d",&n,&D);
    for (int i=0;i<D;++i){
         int x;
         scanf("%d",&x);
         T[i]=x;
    }
    fac[0]=1;
    for (int i=1;i<n;++i)
        fac[i]=fac[i-1]*i%mo;
    ifac[n-1]=qpow(fac[n-1]);
    for (int i=n-2;i>=0;--i)
        ifac[i]=ifac[i+1]*(i+1)%mo;
    powd[0]=1;
    for (int i=1;i<=LEN;++i)
        powd[i]=powd[i-1]*D%mo;
    for (int b=0;b<=LEN;++b){
        for (int a=0;a<D;++a){
            ll t=a*powd[b]%mo,p=1;
            for (int i=0;i<n;++i,p=p*t%mo,t=(t-1+mo)%mo)
                g[b][a][i]=p*ifac[i]%mo;
        }
        for (int i=0;i<n;++i){
            sg[b][0][i]=0;
            for (int a=1;a<D;++a)
                sg[b][a][i]=(sg[b][a-1][i]+(T[a]?g[b][a][i]:0))%mo;
        }
    }
    for (int i=0;i<n;++i){
        input(L[i]),input(R[i]);
        R[i]+=1;
    }
    dfs(0,1);
    ans=(ans%mo+mo)%mo;
    printf("%lld\n",ans);
    return 0;
}

总结

组合数的计数问题,可以考虑通过组合数的递推关系来计算。

原文地址:https://www.cnblogs.com/jz-597/p/12358380.html

时间: 2024-08-30 14:42:55

6476. 【GDOI2020模拟02.19】A的相关文章

6476. 【GDOI2020模拟02.19】A(范德蒙恒等式)

题目描述 题解 镇♂男则反 容斥下界,上界开到大概505位,数位dp最终的和V 设边界(要大于边界)之和为S,那么答案为C(V-S-1,n-1) 根据范德蒙恒等式,C(n+m,k)=∑C(n,i)*C(m,k-i) 如果nm都是正数很好证明,把n+m分成n和m两部分,枚举n部分选择个数组合一下 这个式子其实可以拓展到负数,证明要用生成函数 关于n为负数的组合数:\(C(n,m)=n^{\underline{m}}/m!\),其实和正数时是一样的 (注意这只是为了计算范德蒙恒等式而扩展的,在一般情

6445. 【GDOI2020模拟01.19】String

题目 正解 一听到正解是NTT,我心态崩了. 我特么知道是NTT都不知道该怎么做!哪里像个卷积了? 好吧,是我孤陋寡闻-- 设两个字符串分别为\(A\)和\(B\) 可以考虑试着计算出\(A\)每个子串和\(B\)的相似度(就是位置相同.字母相同的个数),直接统计就完事了. 看到字符集这么小,就可以对于每个字母分开考虑. 假如\(A_i=B_j\),那么以\(A_{i-j+1}\)开头的子串就有\(1\)的贡献. 这样一来,看起来就很像是个卷积了. 搞完之后将贡献加起来,统计即可. 总结 还是没

6447. 【GDOI2020模拟01.19】sort

题目 正解 这题一看也是不能写的题目-- 用个平衡树来维护. 平衡树维护的是一个\(Trie\)的序列.每个\(Trie\)表示这段区间中所有数都会排序一遍. 进行各种操作时,首先会将至多两个节点的\(Trie\)分裂.分裂\(Trie\)会新增\(O(\lg n)\)个节点. 然后将整段区间旋到一棵子树内,然后打标记.平衡树和\(Trie\)上都要打标记. 排序是时候将若干个\(Trie\)合并成一个. 由于这些\(Trie\)是带标记的,所以要将标记下传.\(Trie\)树上标记下传时,如果

6467. 【GDOI2020模拟02.09】西行寺无余涅槃(FWT的性质)

题目描述 题解 吼题 推荐博客:https://www.cnblogs.com/jz-597/p/12300760.html 最暴力的做法是把n个2^m的FWT乘起来,这样显然不行 先把pi,1~k异或上pi,1,把pi,1变为0,最后再把pi,1异或回去 考虑FWT(xor)的本质,tr(A)=(tr(A1)+tr(A2),tr(A1)-tr(A2)),倒着做可以发现只有变换的u和u+2^k(u&2^k=0)只有u+2^k--->u+2^k这一操作时才需要变号 要求FWT(A)i,就等于把

6461. 【GDOI2020模拟02.05】生成树(矩阵树及其扩展、二维拉格朗日插值)

题目描述 给定一张 N 个点,M 条边的无向图,边有红.绿.蓝三种颜色,分别用 1,2,3 表示. 求这张图有多少生成树,满足绿色边数量不超过 x,蓝色边数量不超过 y,答案对10^9 + 7 取模. 1 ≤ N ≤ 40,1 ≤ M ≤ 10^5,1 ≤ ci ≤ 3 行列式 定义矩阵A的行列式det(A)或|A| \(|A|=\sum_{排列p}{(-1)^{p的逆序对个数}\prod{A_{i,p[i]}}}\) 行列式的性质 (\(A\)的转置矩阵\(A^T\):把\(A\)的行列互换)

2017.02.19学习C#的第二天,今天我学到了什么?

2017.02.19,今天是学习C#的第二天,今天学习的是: 1.数据类型:(以下是现阶段编程中最经常用到的类型) (1)整型 (2)浮点型 (3)字符型 (4)布尔型 (5)字符串型 (6)日期时间型 2.变量/常量 3.类型转换 (1)显示转换 (2)隐式转换 4.转义字符 一,基本数据类型介绍 1.整形(主要差别在取值范围) (1)byte 取值范围(0--225),超出后系统报错. (2)short 取之范围:byte < shor t< int      快捷方式为Int16 (3)

Cisco PT模拟实验(19) 路由器的NAT功能配置

Cisco PT模拟实验(19) 路由器的NAT功能配置 实验目的: 掌握NAT网络地址转换的原理及功能 掌握静态NAT的配置,实现局域网访问互联网 掌握广域网(WAN)接入技术的原理 实验背景: 为适应公司不断增长的业务需求,公司欲通过Web服务器发布网站,现要求将内网的服务器IP地址映射为全局IP地址,实现外部网络可以访问公司内部服务器www服务,可在出口路由器上配置NAT功能. 技术原理: 网络地址转换(NAT,Network Address Translation)属于接入广域网(WAN

翻译:Gregory Larsen,2016/02/19(第一版:2014年12月17日)高级T-SQL阶梯1级:使用CROSS JOIN介绍高级T-SQL

原文链接:http://www.sqlservercentral.com/articles/Stairway+Series/119933/ 原文作者:Gregory Larsen,2016/02/19(第一版:2014年12月17日) 系列 本文是"Stairway Series:Stairway to Advanced T-SQL"的一部分 这个阶梯将包含一系列文章,这些文章将在前面两个T-SQL阶梯,T-SQL DML和T-SQL超越基础知识的T-SQL基础上进行扩展. 这个楼梯应

【GDOI2020模拟01.17】小 ω 玩游戏 (容斥+EGF)

小 ω 正在玩一个游戏. 小 ω 有一个 n 行 m 列的网格,初始每个方格中都有数字 0.她需要执行 q 次操作,每次操作可以选择其中一个方格 (x, y),然后先将第 x 行的数全部 +1,接着将第 y 列的数全部 +1. 小 ω 想知道有多少种执行操作的方式能使最后的网格中有不超过 k 个奇数. 两种方式不同当且仅当存在某一步中选择的方格坐标不同. \(1<=n,m<=2e5,q<=10^{18}\) 考虑行列分开,对行,算出\(f(x)\)表示恰好x行奇数的方案数,对列同理,算出