【倍增】LCM QUERY
给一个序列,每次给一个长度l,问长度为l的区间中lcm最小的。
题解:因为ai<60,所以以某个点为左端点的区间的lcm只有最多60种的情况,而且相同的lcm区间的连续的。
所以就想到一个n*60*logn的做法,倍增找出每个点的区间lcm情况,然后修改答案……
1-60的lcm的积大于long long,只能把数拆开,然后比较时用log,结果才用这个数的质因数相乘。
问题在于一开始我对于每个点开个20的数组记录60内第几个质数的个数,这样每次常数就要再乘个20,然后就tle……
优化的方法是位运算,因为只会是2,3,5,7的次幂大于1次,单独记录,其他的只会是0次幂和1次幂。
最后作死的两个小错误:33不是质数……,用ln【60】数组记录log的值然而其中对n取对数……
#include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #define maxn 40000 #define mm 1000000007 #define LL long long #define inf 100000000000 #define rep(i,l,r) for(int i=l;i<=r;i++) #define dow(i,l,r) for(int i=r;i>=l;i--) using namespace std; typedef struct { int v1; double v2; }Big; Big tree[maxn*4]; double ln[maxn]; int f[maxn][17],now; int n,m,maxln,tot=24; LL ans1[maxn],ans2[maxn]; int pri[25]={2,4,8,16, 32,3,9,27, 5,25,7,49, 11,13,17,19, 23,29,31,37, 41,43,47,53,59}; double calc(int x) { double sum=0; rep(i,12,tot) if ((x>>i)&1) sum+=ln[pri[i]]; dow(i,0,4) if ((x>>i)&1) { sum+=(i+1)*ln[2]; break; } if ((x>>7)&1) sum+=3*ln[3]; else if ((x>>6)&1) sum+=2*ln[3]; else if ((x>>5)&1) sum+=ln[3]; if ((x>>9)&1) sum+=2*ln[5]; else if ((x>>8)&1) sum+=ln[5]; if ((x>>11)&1) sum+=2*ln[7]; else if ((x>>10)&1) sum+=ln[7]; return sum; } LL answer(Big x) { LL sum=1; rep(i,12,tot) if ((x.v1>>i)&1) sum=sum*pri[i]%mm; dow(i,0,4) if ((x.v1>>i)&1) { sum=sum*pri[i]%mm; break; } if ((x.v1>>7)&1) sum=sum*27%mm; else if ((x.v1>>6)&1) sum=sum*9%mm; else if ((x.v1>>5)&1) sum=sum*3%mm; if ((x.v1>>9)&1) sum=sum*25%mm; else if ((x.v1>>8)&1) sum=sum*5%mm; if ((x.v1>>11)&1) sum=sum*49%mm; else if ((x.v1>>10)&1) sum=sum*7%mm; return sum; } void build(int x,int l,int r) { tree[x].v2=inf; if (l==r) return; int mid=(l+r)>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); } void change(int x,int l,int r,int ll,int rr,double z) { if (tree[x].v2<=z) return; if (ll<=l && r<=rr) { tree[x].v1=now; tree[x].v2=z; return; } int mid=(l+r)>>1; if (ll<=mid) change(x<<1,l,mid,ll,rr,z); if (rr>mid) change(x<<1|1,mid+1,r,ll,rr,z); } Big ask(int x,int l,int r,int y) { if (l==r) return tree[x]; int mid=(l+r)>>1; Big more; if (y<=mid) more=ask(x<<1,l,mid,y); else more=ask(x<<1|1,mid+1,r,y); if (more.v2<tree[x].v2) return more; return tree[x]; } int main() { rep(i,1,maxn-1) ln[i]=log(i); int tt=0; while (scanf("%d %d",&n,&m)!=EOF) { ++tt; rep(i,1,n) { f[i][0]=0; int k; scanf("%d",&k); dow(j,0,tot) if (k%pri[j]==0) k/=pri[j],f[i][0]+=1<<j; } maxln=floor(ln[n]/ln[2])+1; rep(i,1,maxln) rep(j,1,n+1-(1<<i)) f[j][i]=f[j][i-1]|f[j+(1<<(i-1))][i-1]; build(1,1,n); rep(i,1,n) { int l=i; now=0; while (l<=n) { // printf("!!%d %d\n",now,f[l][0]); now=now|f[l][0]; int r=l; dow(j,1,maxln) if (r-1+(1<<j)<=n && !(~((~f[r][j])|now)) ) r=r-1+(1<<j); change(1,1,n,l-i+1,r-i+1,calc(now)); l=r+1; } } while (m--) { int j; scanf("%d",&j); printf("%lld\n",answer(ask(1,1,n,j))); } } return 0; }
这种区间答案连续的思想并不是第一次遇见了
因疲惫而轻易入眠,是对自己一天努力的最好褒奖。 要和泪子一起努力怀抱梦想对抗残酷的现实……