【JZOJ4586】Ned 的难题

Description

Solution

其实这题是有一个有点暴力方法可以过的,而且速度还快,但是是针对题目贪心,速度当然比正解快。但是我们要为了学知识而做题,并不是为了做题而做题
对于每个数分解质因数,因为是乘法,所以每个质数在区间中的贡献是 xmin(y) ,所以我们用数组fl[i][j]和fr[i][j]来计算每个数的第j个质因数的向左向右贡献区间(保证这段区间的min(y)就是当前这个a[i]的x分解出的次数)。
然后转移显然。
假如算 xy ,y很大怎么办。参见指数取模。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int mo=1000000009,maxn=100005;
int i,j,l,t,k,n,m,x,o,a[maxn];
int fl[maxn][10],fr[maxn][10],f[maxn][10],ci[maxn][10],da,p[maxn],b[maxn];
bool bz[maxn*100];
ll ans;
ll qsm(ll x,ll y){
 ll z=1;
 while(y){
 if(y&1)z=z*x%mo;
 x=x*x%mo;
 y=y/2;
 }
 return z;
}
bool pan(int x,int y,int z){
 if(!x)return 0;int o=0;
 while(x%y==0)x/=y,o++;
 if(o>=z)return 1;return 0;
}
int find(int x,int y){int i;fo(i,1,f[x][0])if(f[x][i]==y)return i;}
int main(){
 freopen("ned.in","r",stdin);
 freopen("ned.out","w",stdout);
 scanf("%d",&n);
 fo(i,1,n)scanf("%d",&a[i]),da=max(da,a[i]);
 fo(i,2,da){
 if(!bz[i])p[++p[0]]=i;
 fo(j,1,p[0]){
 if(i*p[j]>da)break;bz[i*p[j]]=1;
 if(!(i%p[j]))break;
 }
 }
 fo(x,1,n){
 t=a[x];
 fo(i,1,p[0]){
 if(p[i]>sqrt(a[x])+1)break;if(t<p[i])break;
 if(!(t%p[i])){
 f[x][++f[x][0]]=p[i];
 while(!(t%p[i]))t/=p[i],++ci[x][f[x][0]];
 }
 }
 if (t>1)f[x][++f[x][0]]=t,ci[x][f[x][0]]=1;
 }
 ans=1;
 fo(i,1,n)fo(j,1,f[i][0]){
 o=i-1;
 while(pan(a[o],f[i][j],ci[i][j]))o=fl[o][find(o,f[i][j])];
 fl[i][j]=o;
 }
 fod(i,n,1)fo(j,1,f[i][0]){
 o=i+1;
 while(pan(a[o],f[i][j],ci[i][j]+1))o=fr[o][find(o,f[i][j])];
 fr[i][j]=o;
 }
 fo(i,1,n)fo(j,1,f[i][0])ans=ans*qsm(f[i][j],(ll)ci[i][j]*(i-fl[i][j])%(mo-1)*(fr[i][j]-i)%(mo-1))%mo;
 printf("%lld\n",ans);
}

你可能感兴趣的:(dp,指数取模,Ned的难题,模拟链表,暴力优化)