复建练习1—取模与快速幂
A,poj3070
没啥可说的,就是裸的矩阵快速幂
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
struct martix{
int m[2][2];
martix operator*(const martix &b){
martix c;
c.m[0][0]=c.m[1][1]=0;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
c.m[i][j]=(c.m[i][j]+m[i][k]*b.m[k][j])%10000;
return c;
}
martix(){
m[0][0]=m[1][1]=1;
m[1][0]=m[0][1]=0;
}
};
martix quick_pow(martix a,int n){
martix ans;
while(n){
if(n&1){
ans=ans*a;
}
a=a*a;
n>>=1;
}
return ans;
}
int n;
void solve(){
if(n==0){
cout<<0<<'\n';
return ;
}
martix a;
a.m[1][1]=0;
a.m[0][1]=a.m[1][0]=1;
a=quick_pow(a,n);
cout<<a.m[1][0]<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// cout << setprecision(15);
while(cin>>n&&(n+1)){
solve();
}
return 0;
}
B,hdu2817
首先判断下是等差数列还是等比数列然后分开进行计算,等差数列不必多说,等比数列因为K比较大,所以跑个快速幂就可以了
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=200907;
ll quick_pow(ll a,ll n){
ll ans=1;
a=a%mod;
while(n){
if(n&1){
ans=ans*a%mod;
}
n>>=1;
a=a*a%mod;
}
return ans;
}
void solve(){
ll a,b,c,k;
cin>>a>>b>>c>>k;
if(2*b==a+c){
cout<<(a+(b-a)*(k-1)%mod)%mod<<'\n';
}
else{
cout<<a*quick_pow(b/a,k-1)%mod<<'\n';
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
C,hdu1061
直接跑快速幂就可以了
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e6+10;
ll fastPow(ll a, ll n,ll mod)
{
ll ans = 1;
a %= mod;
while (n)
{
if (n & 1)
ans = (ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
void solve(){
int n;
cin>>n;
cout<<fastPow(n,n,10)<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
D,hdu5392
题目不难,题目的意思很迷,搞了好久才明白它的意思,专业一点讲就是给一个置换,求最小置换环。通俗点讲就是起初每个人都站在一个位置,问每个人最少走多少步回到原来位置上,比如a1站在1位置,a2站在2位置,以此类推,接着每个人都可以走一步,但是走一步只能走到特定位置,拿样例第二组来说,a1起初站在位置1,他下一步只能走到位置2,因为位置1上面的数字是2,然后可以从2位置到3,3位置到4,4到5,5到6,6再到1,这系列过程走了6步回到了初始位置,枚举下其他的位置,你会发现他在一个,这个环为1-2-3-4-5-6-1,也就是说这个环长度6,所以最小环为6。再比如第一组样例,1自己在一个环上,2-3在一个环上,一个长度为1一个为2,取最小公倍数2就是答案。
有一个要注意的地方就是,这里面因为要求多个数的lcm,数也比较大,计算过程中必然要取模,所以不能直接求用欧几里得算法求lcm,而应该分解质因数,去每个质因数的最大质数相乘。
hdu好像最近出问题了,好几个题莫名tl了,甚至找了好几份别人以前a了的代码都tl了,我写的代码有点丑,看看就行
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <iomanip>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=3e6+10;
const int mod=3221225473;
ll fastPow(ll a, ll n,ll mod)
{
ll ans = 1;
a %= mod;
while (n)
{
if (n & 1)
ans = (ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
int v[N],p[N];
int prime[N], len = 0;
void get_prime(int n) {
for (int i = 2; i <= n; i++) {
if (v[i] == 0) {
v[i] = i;
prime[len++] = v[i];
}
for (int j = 0; j < len; j++) {
if (prime[j] * i > n || prime[j] > v[i])
break;
v[i * prime[j]] = prime[j];
}
}
}
int a[N];
bool vis[N];
void solve(){
int n;
cin>>n;
get_prime(n);
for(int i=1;i<=n;i++)
cin>>a[i],vis[i]=false;
vector<int>b;
for(int i=1;i<=n;i++){
if(vis[i])
continue;
int len=0,pos=i;
while(!vis[pos]){
len++;
vis[pos]=true;
pos=a[pos];
}
b.push_back(len);
}
map<int,int>mp;
for(int i=0;i<b.size();i++){
n=b[i];
while (n!=1) {
int x = v[n];
int cnt=0;
while (n % x == 0) {
cnt++;
n/=x;
}
mp[x]=max(mp[x],cnt);
}
}
ll ans=1;
for(auto it:mp){
ans=ans*fastPow(it.first,it.second,mod)%mod;
}
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
下面是我觉得挺好看的一份代码
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 3221225473;
const int N = 3e6 + 1;
int a[N], vis[N], num[N];
int main()
{
int t, n;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
memset(vis, 0, sizeof(int) * (n + 1));
memset(num, 0, sizeof(int) * (n + 1));
for (int i = 1; i <= n; i++) {
if (vis[i] == 0) {
// 计算循环长度
int cnt = 0, k = i;
while (vis[k] == 0)
vis[k] = 1, cnt++, k = a[k];
for (int j = 2; j * j <= cnt; j++) {
int sum = 0;
if (cnt % j == 0)
while (cnt % j == 0) cnt /= j, sum++;
num[j] = max(num[j], sum);
}
if (cnt > 1) num[cnt] = max(num[cnt], 1);
}
}
long long ans = 1;
for (int i = 2; i <= n; i++)
while (num[i]--) ans = ans * i % MOD;
printf("%lld\n", ans);
}
return 0;
}
E,hdu3117
这个题不知道斐波那契通项公式还真做不出,因为这题还特意看了不少斐波那契数列的数学性质
当n>49时:
设
其中t<1,k是F(n)的位数
设
又
可得
以上式子可以用来求斐波那契第n项的前面一些数位,比如第n项的前4位数,但是需要注意的是因为通项公式取了近似值,且近似值是大于通项公式的,所以取的位数不能过多
斐波那契数列有个尾数循环性质
斐波那契数列的个位数是一个60步的循环,最后两位数是一个300步的循环,最后三位数是一个1500步的循环,最后四位数是一个15000步的循环,最后五位数是一个150000步的循环。
根据上面两个性质我们就可以做这个题了,首先是前四项,这题难的地方就在这,根据上面的推导,我们可以知道t的大小,让它乘以10000之后就可以得到前面四位了。后面四位最直观的还是快速幂求解,但是因为有尾数循环的性质,我们可以线性预处理一下,然后O(1)查询
注意因为40以内的斐波那契数列在八位数以内,所以40以内的我们直接输出,40往上再分别输出前四位和后四位
最后需要注意的,当取的位数比较多时就不能套这个公式了,这里取的是4位
#include <iostream>
#include <stdio.h>
#include <math.h>
using namespace std;
const int N = 40;
const int N2 = 15000;
int fib[N], f[N2];
void maketable()
{
fib[0] = 0;
fib[1] = 1;
for(int i = 2; i < N; i++)
fib[i] = fib[i - 2] + fib[i - 1];
f[0] = 0;
f[1] = 1;
for(int i = 2; i < N2; i++)
f[i] = (f[i - 2] + f[i - 1]) % 10000;
}
int main()
{
maketable();
int n;
while(cin >> n) {
if(n < N)
cout << fib[n] << endl;
else {
double x = n * log10((1 + sqrt(5)) / 2.0)+log10(1 / sqrt(5));
double y = x - (int)(x) -1;
int ans = pow(10.0, y)*10000;
cout << ans << "...";
printf("%04d\n", f[n % N2]);
}
}
return 0;
}
F,hdu6030
设dp[i] [j]为第i个糖果为j的情况数,j为0或者1
- 因为不可能出现连续两项为0,可以得到dp[i] [1]=dp[i-1] [0]+dp[i-1] [1]
- 因为i为0,所以i-1和i-2必然为2,可得dp[i] [0]=dp[i-3] [0]+dp[i-3] [1]
压缩一下维度得到dp[i]=dp[i-1]+dp[i-3];
因为n比较大,所以用矩阵快速幂加速递推
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
ll _T, n;
struct mat{ ll mat[3][3];};
mat operator *(mat &a, mat &b) {
mat c;
memset(c.mat, 0, sizeof c.mat);
for(int k = 0; k < 3; ++k)
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j] % mod) % mod;
return c;
}
mat power(mat a, ll n) {
mat c;
memset(c.mat, 0, sizeof c.mat); //千万记得初始化
for(int i = 0; i < 3; i++) c.mat[i][i] = 1;
while(n) {
if(n & 1) c = c * a;
n >>= 1;
a = a * a;
}
return c;
}
void run() {
scanf("%lld", &n);
mat a = {1, 1, 0, 0, 0, 1, 1, 0, 0};
mat b = {3, 2, 1, 0, 0, 0, 0, 0, 0};
a = power(a, n - 2);
a = b * a;
printf("%lld\n", a.mat[0][0] % mod);
}
int main() {
scanf("%lld", &_T);
while(_T--) run();
return 0;
}
G,hdu5895
首先第一眼肯定需要进行欧拉降幂,但是难点在于g(n*y)怎么求,网上看到有用g(n)=f(n) *f(n-1)/2来写的,但是要用逆元来写,感觉蛮麻烦的,后面想了个矩阵转移感觉还行。
首先我们对两个式子展开可以得到f(n) ^ 2=4f(n-1)f(n-2)+f(n-2) ^ 2+4f(n-1) ^ 2,f(n)f(n-1)=2f(n-1)^2+f(n-1)*f(n-2)接着我们就可以愉快地转移了
首先是转移矩阵base={ {1,4,4,1},{0,4,4,1},{0,2,1,0},{0,1,0,0} },然后是答案矩阵的形式a= {{g(n)},{f(n) ^ 2},{f(n-1)*f(n)},{f(n-1) ^ 2}},很显然,答案矩阵为base ^ (n-1) *a,然后我们就能得到了g(n *y)了,然后就是欧拉降幂公式了,令n=g(n *y),s=s+1,把,那么x^ n%s=(x^(n%phi(s)+phi(s)))%s。注意这个地方求欧拉函数看起来只有1e8范围,可以直接预处理,但实际上会me,所以我们需要直接计算欧拉函数,这一步用欧拉函数通项公式就行了,根号n复杂度
欧拉降幂这一步看到有人因为s比较大不能直接预处理出来,采用了中国剩余定理
然后看了眼他的代码,兜兜转转求欧拉函数还是回到了试除法求欧拉函数,多多少少这个剩余定理用的有点鸡肋,不过还是有点启发,可以对除数进行分解然后转换为同余方程组求解。
ll phi(ll n) {
ll ans = n;
for (ll i = 2; i * i <= n; ++i) {
if (n % i) continue;
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n != 1) ans = ans / n * (n - 1);
return ans;
}
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e8 + 10;
ll base[4][4] = { {1,4,4,1},{0,4,4,1},{0,2,1,0},{0,1,0,0} };
ll phi(ll n) {
ll ans = n;
for (ll i = 2; i * i <= n; ++i) {
if (n % i) continue;
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n != 1) ans = ans / n * (n - 1);
return ans;
}
ll mod;
struct martix {
ll m[4][4];
martix operator*(const martix& p) {
martix c;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k++) {
c.m[i][j] = (m[i][k] * p.m[k][j] + c.m[i][j]) % mod;
}
}
return c;
}
martix() {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
m[i][j] = 0;
}
};
martix quick_pow(ll n) {
martix a,ans;
for (int i = 0; i < 4; i++)
ans.m[i][i] = 1;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
a.m[i][j] = base[i][j];
while (n) {
if (n & 1) {
ans = ans * a;
}
a = a * a;
n >>= 1;
}
return ans;
}
ll quick_pow(ll a, ll n) {
ll ans = 1;
while (n) {
if (n & 1)
ans = ans * a % mod;
a = a * a % mod;
n >>= 1;
}
return ans;
}
void solve() {
ll n, y, s, x;
cin >> n >> y >> x >> s;
n *= y;
s = s + 1;
if (n == 0) {
cout << 0 << '\n';
return;
}
mod = phi(s);
martix ans = quick_pow(n-1);
ll a[4][1] = {1,1,0,0}, b[4][1] = {0,0,0,0};
for (int i = 0; i < 4; i++)
for (int j = 0; j < 1; j++)
for (int k = 0; k < 4; k++)
b[i][j] = (b[i][j] + ans.m[i][k] * a[k][j]) % mod;
n = b[0][0]+phi(s);
mod = s;
cout << quick_pow(x, n) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
H,hdu5564
这题真的思维有点难,思路大概如下,有一些错误就是dp方程左边的的i应该为i+1
其实方程不难想,但是把维度压缩的方式和对第70列进行处理从而求的前缀和没遇到过是真的蛮难想到的
而且写代码时不注意也会犯很多细节错误,我调了一下午才过。。。写的也比较丑
贴一份别人的代码吧
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef long long ll;
typedef vector<int> vi;
#define xx first
#define yy second
#define rep(i, a, n) for(int i = a; i < n; i++)
#define vep(c) for(decltype((c).begin()) it = (c).begin(); it != (c).end(); it++)
int mod = int(1e9) + 7, INF = 0x3fffffff, maxn = 1e5 - 1;
ll st[71], ed[71];
int compressor(int i, int j)
{
return i * 7 + j;
}
class matrix
{
public:
ll v[71][71];
matrix(void) {
memset(v, 0, sizeof(v));
}
matrix operator*(matrix& a) {
matrix ans, b = *this;
rep(i, 0, 71) {
rep(j, 0, 71) {
rep(k, 0, 71) {
ans.v[i][j] = (ans.v[i][j] + b.v[i][k] * a.v[k][j]) % mod;
}
}
}
return ans;
}
matrix operator^(int b) {
matrix ans, a = *this;
if (b < 0) return ans;
rep(i, 0, 71) ans.v[i][i] = 1;
while (b) {
if (b & 1) ans = ans * a;
a = a * a;
b >>= 1;
}
return ans;
}
};
int main(void)
{
int T;
scanf("%d", &T);
rep(i, 1, 10) st[compressor(i, i % 7)]++;
while (T--) {
int l, r, m;
scanf("%d%d%d", &l, &r, &m);
matrix x;
rep (l, 0, 10) {
rep (j, 0, 10) {
if (l + j == m) continue;
rep (k, 0, 7) {
x.v[compressor(j, k)][compressor(l, (k * 10 + l) % 7)] = 1;
}
}
}
rep (i, 0, 10) x.v[compressor(i, 0)][70] = 1;
x.v[70][70] = 1;
matrix x1 = x ^ (r - 1);
memset(ed, 0, sizeof(ed));
rep (i, 0, 71) {
rep (j, 0, 71) {
ed[i] += st[j] * x1.v[j][i] % mod;
}
}
ll ansr = ed[70];
rep (i, 0, 10) ansr = (ansr + ed[compressor(i, 0)]) % mod;
matrix x2 = x ^ (l - 2);
memset(ed, 0, sizeof(ed));
rep (i, 0, 71) {
rep (j, 0, 71) {
ed[i] += st[j] * x2.v[j][i] % mod;
}
}
ll ansl = ed[70];
rep (i, 0, 10) ansl = (ansl + ed[compressor(i, 0)]) % mod;
printf("%lld\n", (ansr - ansl + mod) % mod);
}
return 0;
}
I,hdu2243
看题解是个AC自动机+矩阵的题,不会自动机,贴一份别人的代码,等学了自动机再来看看
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <ll,ll> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define all(x) x.begin(),x.end()
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a)
#define mp make_pair
#define dd(x) cout<<#x<<"="<<x<<" "
#define de(x) cout<<#x<<"="<<x<<"\n"
#define debug() cout<<"I love Miyamizu Mitsuha forever.\n"
const int inf=0x3f3f3f3f;
const int maxn=105;
class matrix
{
public:
ull arrcy[35][35];
int row,column;
matrix()
{
memset(arrcy,0,sizeof arrcy);
column=row=0;
}
friend matrix operator *(matrix s1,matrix s2)
{
int i,j;
matrix s3;
for (i=0;i<s1.row;i++)
{
for (j=0;j<s2.column;j++)
{
for (int k=0;k<s1.column;k++)
{
s3.arrcy[i][j]+=s1.arrcy[i][k]*s2.arrcy[k][j];
}
}
}
s3.row=s1.row;
s3.column=s2.column;
return s3;
}
void show()
{
for(int i=0;i<row;i++)
{
for (int j=0;j<column;j++)
cout<<arrcy[i][j]<<" ";
cout<<endl;
}
}
}mat,mul;
matrix quick_pow(matrix s1,long long n)
{
matrix mul=s1,ans;
ans.row=ans.column=s1.row;
memset(ans.arrcy,0,sizeof ans.arrcy);
for(int i=0;i<ans.row;i++)
ans.arrcy[i][i]=1;
while(n)
{
if(n&1) ans=ans*mul;
mul=mul*mul;
n>>=1;
}
return ans;
}
ull qpow(ull a,ull b)
{
ull ans=1;
for(;b;b>>=1,a=a*a)
if(b&1) ans*=a;
return ans;
}
class Trie
{
public:
Trie()
{
cnt=1;
}
int cnt;
int trie[maxn][26];
int fail[maxn];
bool bad[maxn];
void init()
{
rep(i,0,cnt)
{
fail[i]=bad[i]=0;
rep(j,0,26) trie[i][j]=0;
}
cnt=1;
}
void insert(string s)
{
int len=s.size();
int pos=0;
rep(i,0,len)
{
int next=s[i]-'a';
if(!trie[pos][next]) trie[pos][next]=cnt++;
pos=trie[pos][next];
}
bad[pos]=1;
}
void getfail()
{
queue<int> q;
rep(i,0,26)
{
if(trie[0][i])
{
fail[trie[0][i]]=0;
q.push(trie[0][i]);
}
}
while(!q.empty())
{
int pos=q.front();
q.pop();
rep(i,0,26)
{
bad[pos]|=bad[fail[pos]];
if(trie[pos][i])
{
fail[trie[pos][i]]=trie[fail[pos]][i];
q.push(trie[pos][i]);
}
else trie[pos][i]=trie[fail[pos]][i];
}
}
}
}ac;
string s;
ull cal(ll x)
{
matrix l,r;
r.row=2;r.column=1;
r.arrcy[0][0]=0;
r.arrcy[1][0]=1;
l.row=l.column=2;
l.arrcy[0][0]=l.arrcy[0][1]=26;
l.arrcy[1][0]=0;l.arrcy[1][1]=1;
r=quick_pow(l,x)*r;
return r.arrcy[0][0];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,l;
while(cin>>n>>l)
{
ac.init();
mes(mul.arrcy,0);mes(mat.arrcy,0);
rep(i,0,n)
{
cin>>s;
ac.insert(s);
}
ac.getfail();
mat.row=1;
mat.column=ac.cnt+1;
mat.arrcy[0][0]=1;
mul.row=mul.column=ac.cnt+1;
rep(i,0,ac.cnt)
{
if(ac.bad[i]) continue;
rep(j,0,26)
{
if(!ac.bad[ac.trie[i][j]]) mul.arrcy[i][ac.trie[i][j]]++;
}
}
rep(i,0,mul.column) mul.arrcy[i][mul.column-1]=1;
mat=mat*quick_pow(mul,l);
ull ans=0;
rep(i,0,mat.column) ans=(ans+mat.arrcy[0][i]);
ans--;
ull cnt=cal(l);
cnt-=ans;
cout<<cnt<<"\n";
}
return 0;
}