zoukankan      html  css  js  c++  java
  • 【bzoj4818】[Sdoi2017]序列计数 矩阵乘法

    原文地址:http://www.cnblogs.com/GXZlegend/p/6825132.html


    题目描述

    Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数。Alice还希望,这n个数中,至少有一个数是质数。Alice想知道,有多少个序列满足她的要求。

    输入

    一行三个数,n,m,p。
    1<=n<=10^9,1<=m<=2×10^7,1<=p<=100

    输出

    一行一个数,满足Alice的要求的序列数量,答案对20170408取模。

    样例输入

    3 5 3

    样例输出

    33


    题解

    矩阵乘法

    至少有1个质数的方案数=总方案数-不含质数的方案数

    可以先在O(m)的时间内把1~m的质数筛出来。

    然后考虑:从mod p=0,到mod p=0,可以由mod p=a和mod p=p-a两个阶段组成。

    可以设f[i][j]表示从mod p=i到mod p=j的方案数,不难看出这是一个矩阵,而且自乘m次就能得到答案。

    所以只要处理出2种f即可。

    对于每个数可以更新所有的f[i](0≤i<n),不过这样会TLE

    其实这样做没有必要,因为f都是循环出现的,只需要求f[0]即可,再据此推其余的f。

    代码中,我将mod p=0当作了p来处理,其实看作0也是一样的。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define mod 20170408
    using namespace std;
    typedef long long ll;
    bool notprime[20000010];
    int pri[5000010] , top;
    struct matrix
    {
    	int n , m;
    	ll num[110][110];
    	matrix()
    	{
    		n = m = 0;
    		memset(num , 0 , sizeof(num));
    	}
    	matrix operator*(const matrix a)const
    	{
    		matrix ans;
    		ans.n = n , ans.m = a.m;
    		int i , j , k;
    		for(i = 1 ; i <= ans.n ; i ++ )
    			for(j = 1 ; j <= ans.m ; j ++ )
    				for(k = 1 ; k <= m ; k ++ )
    					ans.num[i][j] = (ans.num[i][j] + num[i][k] * a.num[k][j]) % mod;
    		return ans;
    	}
    }A , B;
    matrix pow(matrix x , int y)
    {
    	matrix ans;
    	int i;
    	ans.n = x.n , ans.m = x.m;
    	for(i = 1 ; i <= ans.n ; i ++ )
    			ans.num[i][i] = 1;
    	while(y)
    	{
    		if(y & 1) ans = ans * x;
    		x = x * x , y >>= 1;
    	}
    	return ans;
    }
    int main()
    {
    	int n , m , p , i , j;
    	scanf("%d%d%d" , &n , &m , &p);
    	notprime[1] = 1;
    	for(i = 2 ; i <= m ; i ++ )
    	{
    		if(!notprime[i]) pri[++top] = i;
    		for(j = 1 ; j <= top && i * pri[j] <= m ; j ++ )
    		{
    			notprime[i * pri[j]] = 1;
    			if(i % pri[j] == 0) break;
    		}
    	}
    	A.n = A.m = B.n = B.m = p;
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		A.num[p][(i - 1) % p + 1] ++ ;
    		if(notprime[i]) B.num[p][(i - 1) % p + 1] ++ ;
    	}
    	for(i = p - 1 ; i >= 1 ; i -- )
    		for(j = 1 ; j <= p ; j ++ )
    			A.num[i][j] = A.num[i + 1][j % p + 1] , B.num[i][j] = B.num[i + 1][j % p + 1];
    	printf("%lld
    " , (pow(A , n).num[p][p] - pow(B , n).num[p][p] + mod) % mod);
    	return 0;
    }

     

  • 相关阅读:
    常忘知识点一:嵌套属性
    button按钮的状态为disabled禁用状态,click事件无法触发,但是为什么touchstart下却依然可以触发
    shell
    sql help cs
    再次写给我们这些浮躁的程序员
    C# winform进度条 (异步)
    关于C# WinForm中进度条的实现方法
    Oracle 多行记录合并/连接/聚合字符串的几种方法
    PL/SQL编码规范的一些建议
    PL/SQL 日期时间类型函数及运算
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6825132.html
Copyright © 2011-2022 走看看