泛刷水题记17.12.22

11 篇文章 0 订阅
4 篇文章 0 订阅

目前处于康复期,所以做多一点题目……感觉自己码力退步了QAQ连初二的时候都比不上了(现在连LCT都写不动了……颓颓颓)

LOJ 2038 「SHOI2015」超能粒子炮・改

题意:求 i=0k(ni)(mod2333) ,其中 n,k1018
分析:一般组合数取模的题目我们都可以马上想到Lucas定理,而这题我们稍微转化下就可以了
题解:先化一波式子,由于 2333 是质数,那么原式即

S(n,k)=i=0k(ni)(mod2333)

=i=0k(n/2333i/2333)(n%2333i%2333)(mod2333)

然后 i%2333 有一个模运算的周期性,同时这里面 i/2333 又是相等的,我们考虑分块处理,只需要考虑再考虑最后不单独成一块的那一部分,暴力即可
成整块的前一部分是 i=0k23331(n/2333i)×i=02332(n%2333i)
然后剩下来的就是 i=0k%2333(n/2333i/2333)(n%2333k%2333)
我们将 S(n,k) 代进去,得到
S(n,k)=S(n/2333,k23331)×S(n%2333,2332)+S(n%2333,k%2333)(n/2333k/2333)
同时预处理下 1 2333范围内的组合数,利用Lucas递归到 2333 范围里面,而 S(n,k) 2333 范围内预处理,大范围递归计算就好了,复杂度类似Lucas定理,为 O(log2n)

# include <cstdio>
using namespace std;
const int mod = 2333;

int c[mod+20][mod+20], f[mod+20][mod+20];

inline int lucas(long long i, long long j){
    if (i < mod && j < mod) return c[i][j];
    return c[i%mod][j%mod]*lucas(i/mod, j/mod)%mod;
}

inline int calcCsum(long long i, long long j){
    if (i < mod && j < mod) return f[i][j];
    return (f[i%mod][mod-1]*calcCsum(i/mod, (j+1)/mod-1)+lucas(i/mod, j/mod)*f[i%mod][(j+1)%mod-1])%mod;
}

int main(){
    c[0][0] = 1;
    for (int i = 1; i < mod; i++)
        for (int j = 0; j <= i; j++)
            c[i][j] = (c[i-1][j]+(j>=1)*c[i-1][j-1])%mod;
    for (int i = 0; i < mod; i++) {
        f[i][0] = 1;
        for (int j = 1; j < mod; j++)
            f[i][j] = (f[i][j-1]+c[i][j])%mod;
    }
    int T; scanf("%d", &T);
    while (T--) {
        long long n, m;
        scanf("%lld%lld", &n, &m);
        printf("%d\n", calcCsum(n, m));
    }
    return 0;
}

LOJ2028 「SHOI2016」随机序列

题意:一个序列 ai ,两个相邻数之间可以插入加号、减号或乘号,每次不断修改其中某个数并询问所有可能的表达式的和
分析:先考虑没修改怎么做,考虑一个DP,设 f(i) 表示前 i 个数能组成的表达式的和,g(i)表示方案数,那么插入一个加号和减号就直接乘方案数,插入乘号的话考虑往前 j 个是乘号,那么从f(ij)转移,再前一个是加号和减号分别讨论即可
但是这是第一眼想的思路,稍微再想想我们会发现这好像想多了……每一个加号都会有一个减号消掉他,所以只有第一个是有贡献的,那么就是剩下的方案数和前面的乘积和,即 i=1n2×3nij=1iaj
一开始可以暴力统计一遍,然后每次修改可以直接用逆元把后面的给做掉,相当于后面的区间乘一个数,用线段树维护前缀乘积就好了
code没调,先存着

# include <cstido>
# define M 1000000007
# define ms(x) (x>=mod?x-mod:x)
using namespace std;
namespace SegT{
    int sum[S],tag[S];
    inline void pup(int x){ sum[x]=ms(sum[x<<1]+sum[x<<1|1]); }
    inline void ptg(int x,int d){ sum[x]=(sum[x]*d)%mod; }
    inline void pdn(int x){ if (tag[x]!=1) ptg(x<<1,tag[x]),ptg(x<<1|1,tag[x]); tag[x]=1;  }
    void init(int x,int l,int r){
        if (l==r){ sum[x]=p[l]*a[l]; tag[x]=1; return; }
        if (l<r){
            tag[x]=1;
            int m = (l+r)>>1;
            init(x<<1,l,m),init(x<<1|1,m+1,r);
            pup(x);
        }
    }
    int opt,opans,opl,opr,opg;
    void tend(int x,int l,int r){
        if (opl <= l && r <= opr){      //in intervals
            if (opt == 1){ opans = ms(opans+sum[x]*tag[x]); }
            if (opt == 2){ tag[x]=(tag[x]*opg)%mod; }
        }
        else{
            int m=(l+r)>>1; pdn(x);
            if (opl <= m) tend(x<<1,l,m);
            if (opr > m)  tend(x<<1|1,m+1,r);
            pup(x);
        }
    }
}
int a[N],n,q;
int main(){
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;++i) scanf("%d",a+i);
    for (int i=2;i<=n;++i) a[i] = (a[i] * a[i-1]) % mod;
    p[n]=2; for (int i=1;i<n;++i) p[i]=ms(p[i+1]+ms(p[i+1]+p[i+1]));
    SegT::init(1,1,n);
    for (int i=0;i<q;++i){
        int t,v; scanf("%d%d",&t,&v);
        SegT::opt=2; SegT::opl=SegT::opr=t; SegT::opg=v;
        SegT::tend(1,1,n);
        SegT::opt=1; SegT::opl=1,SegT::opr=n; SegT::opans=0;
        SegT::tend(1,1,n);
        printf("%d\n",SegT::opans);
    }
    return 0;
}

「CodePlus 2017 12 月赛」可做题2

题意:问对于第一项是给定的 i ,第二项x[l,r]内,以此构成的Fibonacci数列里指定的第 k 项有多少种情况在模p意义下为 m
分析:第一眼,发现这里中间Fibonacci的转移矩阵是相同的!于是可以直接快速幂求出Fk=a11i+a12x,然后我们再推推式子就是 ma11ia12x(modp) ,用扩欧解一下 a12+pk=ma11i 就好了,实现上有不少坑点,例如 i 需要模p……另外需要注意 ma11i 有可能小于 0 <script type="math/tex" id="MathJax-Element-984">0</script>,需要搞回来
另外还有Fibonacci数列取模循环节的方法,这里就暂且不提了

# include <cstring>
# include <iostream>
# include <cstdio>
# include <cmath>
# define mem(x,v) memset(x,v,sizeof(x))
# define ms(x) (x>=p?x-p:x)
using namespace std;
typedef long long LL;
LL i,l,r,k,p,m;
struct matrix{
    LL a[2][2];
    matrix(){ mem(a,0); }
    matrix operator*(const matrix t){
        matrix ans;
        for (int i=0;i<2;++i)
            for (int j=0;j<2;++j)
                for (int k=0;k<2;++k)
                    ans.a[i][j]=(ans.a[i][j]+(a[i][k]*t.a[k][j])%p)%p;
        return ans;
    }
    void print(){
        cerr << a[0][0] <<' ' << a[0][1] << endl;
        cerr << a[1][0] <<' ' << a[1][1] << endl;
    }
}; 
matrix fast_pow(matrix x,LL k){
    matrix t; t.a[0][0]=t.a[1][1]=1;
    for ( ;k;k>>=1,x=x*x) if (k&1) t=t*x;
    return t;
}
void extgcd(LL a,LL b,LL&d,LL &x,LL &y){
    if(!b){d=a;x=1,y=0;}
    else{
        extgcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}
int main(){
//  freopen("inp.in","r",stdin);
//  freopen("myp.out","w",stdout);
    matrix fib; fib.a[0][1]=fib.a[1][0]=fib.a[1][1]=1;
    int T; cin >> T;
    while (T--){
        cin >> i >> l >> r >> k >> p >> m;
        i%=p;
        matrix t=fast_pow(fib,k-1); //t.print(); cout << endl;
    //  swap(t.a[0][1],t.a[0][0]) ;
        LL d,k,a2; extgcd(p,t.a[0][1],d,k,a2);
        LL c=(m - t.a[0][0]*i);
        if (c < 0){ c = p - ((-c) % p); }
        if(c % d != 0) cout << 0 << endl;
        else{
            //a2+pk=21 ? 
            a2 *= c/d; k*= c/d;
            a2 += ((l-a2) / (p/d)) * (p / d);
            while (a2 > l){ a2 -= p/d; }
            while (a2 < l){ if (a2 > r){cout << 0 << endl; continue;} a2 += p/d; }
        //  cerr << "Now l=" << l <<"  now a2=" << a2 << " now spa = " << p << '/' << d << endl;
        //  cerr << (r-a2) << "\n" << (p/d) << endl;
            LL num = (((r-a2) / (p/d))+1); cout << (r>=a2?num:0) << endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值