hdu5726
hdu5726
题目难点 :
给你一个不变的序列,输入很多k,输出区间gcd等于k的区间的数量。
对所有区间计数,枚举左端点,二分右端点即可。
理论依据: 以任意左端点为起点的所有区间的gcd的种类不会超过这个点的值的因子的个数。
细节: 区间询问使用ST表.
#include<bits/stdc++.h> using namespace std; #define ll long long const ll maxn=1e5+5; map<ll,ll>mp; ll t,n,q; ll a[maxn]; ll mm[maxn],rmq[maxn][20]; void st(ll l,ll r){ mm[0]=-1; for(ll i=1;i<maxn;i++){ mm[i]=((i&i-1)==0)?mm[i-1]+1:mm[i-1]; }//如果卡常,可以放外面 for(ll i=l;i<=r;i++)rmq[i][0]=a[i]; for(ll j=1;j<=mm[r-l+1];j++) for(ll i=l;i+(1<<j)-1<=r;i++) rmq[i][j]=__gcd(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]); } ll query(ll l,ll r){ ll k=mm[r-l+1]; return __gcd(rmq[l][k],rmq[r-(1<<k)+1][k]); } void ini() { for(ll i=1; i<=n; i++) { ll l,r=i-1; // cout<<i<<":"; ///100 while(r<n){// r : gcd[i..r]!=gcd[i,r+1] // cout<<r<<" "; ll pre_r=r; l=r+2,r=n+1; ll gcd=query(i,l-1); ///lg while(l<r){ ll mid=(l+r)>>1; ///lg if(mid>n||gcd!=query(i,mid)){ r=mid;// query(i,r)!=gcd } else l=mid+1;//query(i,l-1)=gcd }// query(i,l-1)=gcd&&query(i,l)!=gcd mp[gcd]+=(l-1)-(pre_r+1)+1; r=l-1; } // cout<<endl; } } int main() { scanf("%lld",&t); for(ll time=1; time<=t; time++) { mp.clear(); scanf("%lld",&n); for(ll i=1;i<=n;i++)scanf("%lld",a+i); st(1,n); ini(); scanf("%lld",&q); printf("Case #%lld:\n",time); for(ll i=1; i<=q; i++) { ll l,r; scanf("%lld %lld",&l,&r); ll ans=query(l,r); printf("%lld ",ans); printf("%lld\n",mp[ans]); } } }
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Believe it!