模擬賽 計算(時間限制:1s;空間限制 64MB)

題目描述

求(n^1+n^2+n^3+......+n^m)%p的值。

樣例輸入

2 2 5

樣例輸出

1

數據範圍

n,p<=10^8,m<=10^17

題解

先說一下,這道題通法是矩陣乘法(話說……noip考這個?)。
linux機子上測的,只有快速冪30分。這裏沒打……
50分的打法用到了分治算法,原式可化爲(n^1+n^2+n^3+......+n^(m div 2))+n^(m div 2)*(n^1+n^2+n^3+......+n^(m div 2))(紅色部分要根據m的奇偶性特判)對於n^k可以快速冪過。剩下兩部分可以繼續分治下去直到括號內只剩n^1。這樣看似複雜度很低,但應爲每次分治都需要走到底,所以只是一種比普通暴力優越一點的算法。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define MAXN 100005
#define ll long long
using namespace std;
ll n,p,m,ans;
ll ksm(ll x,ll y)
{
	ll da=1;
	while(y>0)
	   {if(y&1) da=(da*x)%p;
	    x=(x*x)%p;
	    y=y>>1;
	   }
	return da;
}
ll work(ll s)
{
	if(s==1) return n%p;
	ll sum,t;
	if(s%2==1)
	   {t=work(s/2)%p;
	    sum=(t+(ksm(n,s/2)%p)*(work((s+1)/2)%p)%p)%p;
	   }
	else
	   {t=work(s/2)%p;
	    sum=(t+((ksm(n,s/2)%p)*t)%p)%p;
	   }
	return sum;
}

int main()
{
	freopen("calc.in","r",stdin);
	freopen("calc.out","w",stdout);
	scanf("%lld%lld%lld",&n,&m,&p);
	n=n%p;
	ans=work(m);
	printf("%lld\n",ans);
	return 0;
}
但是,這種所發有一個很大的優化空間,即以k爲關鍵字,記錄(n^1+n^2+n^3+......+n^k)%p的值。這樣能節省不少時間,我的做法是對k值進行hash。加上這個優化數據全部秒過……
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define MAXN 100005
#define ll long long
#define SU 49747
#define ad 7
using namespace std;
ll n,p,m,ans;
ll sh[500002];
ll ksm(ll x,ll y)
{
	ll da=1;
	while(y>0)
	   {if(y&1) da=(da*x)%p;
	    x=(x*x)%p;
	    y=y>>1;
	   }
	return da;
}
ll work(ll s)
{
	int loc=(s%SU)*ad;
	if(sh[loc]!=-1) return sh[loc];
	if(s==1) return n%p;
	ll sum,t;
	if(s%2==1)
	   {t=work(s/2)%p;
	    sum=(t+(ksm(n,s/2)%p)*(work((s+1)/2)%p)%p)%p;
	   }
	else
	   {t=work(s/2)%p;
	    sum=(t+((ksm(n,s/2)%p)*t)%p)%p;
	   }
	sh[loc]=sum;
	return sum;
}

int main()
{
	freopen("calc.in","r",stdin);
	freopen("calc.out","w",stdout);
	scanf("%lld%lld%lld",&n,&m,&p);
	n=n%p;
	memset(sh,-1,sizeof(sh));
	ans=work(m);
	printf("%lld\n",ans);
	return 0;
}
以下是正解:矩陣乘法。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define MAXN 100005
#define ll long long
#define SU 49747
#define ad 7
using namespace std;
ll n,p,m,answer;
ll a[5],b[3][3],c[3][3];
void mul(ll A[3][3],ll B[3][3],ll ans[3][3])
{
	ll t[3][3];
	int i,j,k;
	for(i=1;i<=2;i++)
	for(j=1;j<=2;j++)
	   {t[i][j]=0;
		for(k=1;k<=2;k++)
	       t[i][j]=(t[i][j]+A[i][k]*B[k][j]%p)%p;
	   }
	for(i=1;i<=2;i++)
	for(j=1;j<=2;j++)
	   ans[i][j]=t[i][j];
}
void ksm(ll x)
{
	int i,j;
	for(i=1;i<=2;i++) c[i][i]=1;
	while(x>0)
	   {if(x&1) mul(b,c,c);
	    mul(b,b,b);
	    x=x>>1;
	   }
}
int main()
{
	freopen("calc.in","r",stdin);
	freopen("calc.out","w",stdout);
	scanf("%I64d%I64d%I64d",&n,&m,&p);
	n=n%p;
	if(m==1) {printf("%I64d\n",n); return 0;}
	a[1]=a[2]=n;
	b[1][1]=n; b[2][1]=1; b[2][2]=1;
	ksm(m-1);
	for(int i=1;i<=2;i++)
	   answer=(answer+(a[i]*c[i][1])%p)%p;
	printf("%I64d\n",answer);
	return 0;
}
發佈了367 篇原創文章 · 獲贊 2 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章