zoukankan      html  css  js  c++  java
  • 离线求逆元

    我们先看一道

    看到这道题,我们首先考虑线性求逆的复杂度\(O(max(a_{i}))\),在这道题中显然不是很优,因为时间空间都会被卡(而且出题人也不傻,怎么可能乘法逆元一的正解就是乘法逆元二的呢)

    我们再考虑一下费马小定理和拓展欧几里得的\(O(n\ (log\ p))\),貌似更炸.

    然后我们讲完离线逆元之后我们再考虑一下这个神奇的算法的可行性

    首先我们要知道一个事情就是:前缀积的逆元就是逆元的前缀积(即逆元是完全积性的)

    证:我们设任意两个整数\(a\)\(b\),我们看一下他们在模\(p\)意义下一定满足\(a^{-1}*b^{-1}\equiv (a*b)^{-1}\ (mod\ p)\)

    我们根据逆元的定义可以得到柿子\(a*a^{-1}*b*b^{-1}\equiv 1\ (mod\ p)\)

    然后我们把\(a*b\)看做一个整体,然后这个整体的逆元就是\((a*b)^{-1}\)

    我们可以知道\((a*b)*(a*b)^{-1}\equiv 1\ (mod\ p)\)

    \((a^{-1}*b^{-1})\equiv (a*b)^{-1}\ (mod\ p)\)

    根据这个性质,我们可以用一个\(pre_{n}\)数组来存储\(a_{n}\)的前缀积(不是前缀和!!!

    \(\displaystyle pre_{n}=\prod_{i=1}^{n}a_{i}\)

    \(\displaystyle pre_{n}^{-1}=\prod_{i=1}^{n}a_{i}^{-1}\)

    然后我们就会发现:

    \(a_{i}^{-1}=pre_{i}^{-1}*pre_{i-1}\ (1<=i<=n)\)

    \(pre_{i}^{-1}=pre_{i+1}^{-1}*a_{i+1}\ (1<=i<n)\)

    然后我们是不是阔以根据这两个柿子来做题了呢

    我们考虑一下他的时间复杂度:线性的枚举\(O(n)\)+费马小定理(或拓展欧几里得)求\(pre_{n}^{-1}\)\(O(log\ p)\)

    时间复杂度为:\(O(n+log\ p)\)

    但是这道题还会卡常,所以我们还要用快读

    因为这道题最后要求的是\(\displaystyle\sum_{i=1}^{n}{\frac {k^{i}}{a_{i}}}\),所以我们可以转换为\(\displaystyle \sum_{i=1}^{n}{k^{i}*a_{i}^{-1}}\)

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define LL long long 
    const LL p = 1e9+7;
    LL n,ans,a[5000010],pre[5000010],inv[5000010];
    inline LL read()
    {
    	int s = 0, w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
    	return s * w; 
    }
    LL ksm(LL a, LL b)
    {
    	LL res = 1;
    	for(; b; b >>= 1)
    	{
    		if(b & 1) res = res * a % p;
    		a = a * a % p;
    	}
    	return res;
    }
    int main()
    {
    	n = read(); pre[0] = 1;
    	for(int i = 1; i <= n; ++i)
    	{
    		a[i] = read();
    		pre[i] = pre[i-1] * a[i] % p; 
    	}
    	inv[n] = ksm(pre[n],p-2);
    	for(int i = n; i >= 1; --i)
    	{
    		inv[i-1] = inv[i] * a[i] % p;
    	} 
    	LL tmp = 1;
    	for(int i = n; i >= 1; --i)
    	{
    		ans = (ans + inv[i] * pre[i-1] % p * tmp % p) % p;
    		tmp = tmp * 998244353 % p; 
    	}
    	printf("%lld\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    node实现将打包后的文件转压缩包
    Git/SVN忽略node_modules文件
    node实现发送邮件
    node搜索文件夹下的指定内容
    node批量修改文件文本内容
    微信小程序上线发布需要做的事情
    两件事 Jquery.form 锁
    .NET MVC 提交表单出现检测到有潜在危险的Request.Form值
    第一次使用TinyMCE
    第一次使用Entity Framework 的CodeFirst
  • 原文地址:https://www.cnblogs.com/genshy/p/13444098.html
Copyright © 2011-2022 走看看