題意:給定長度爲n的數組A和Q個詢問。詢問[L,R],求A(L)%A(L+1)%A(L+2)%……%A(R) 。其中 1≤N,Q≤100000
一個性質:大數對小數取餘,結果小於大數的一半。所以每個大數最多對小數取log(ai)次
題解一:轉自點擊打開鏈接
已知mod操作類似gcd操作,結果是單調的,只會小不會大。
把所有詢問預存,按左邊界排序,當前左邊界存在於詢問時,加入優先隊列,對於當前位置,優先隊列中大於a[i]的都對a[i]取餘,取到 < a[i]即可停止,更小的肯定更無變化,根據右邊界拋出即可
複雜度爲O(nlgnlgn)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int N=1e5+10;
struct Node{
int R,v,n;
Node(){}
Node(int R,int v,int n):R(R),v(v),n(n){}
bool operator <(const Node&a)const{
return v<a.v;
}
};
struct Sec{
int L,R,n;
Sec(){}
Sec(int L,int R,int n):L(L),R(R),n(n){}
}c[N];
int a[N],n,m;
int Ans[N];
priority_queue<Node>Q;
bool cmp(Sec i,Sec j){return i.L<j.L;}
void work()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)scanf("%d",&a[i]);
scanf("%d",&m);
for (int i=1;i<=m;i++)scanf("%d%d",&c[i].L,&c[i].R),c[i].n=i;
sort(c+1,c+m+1,cmp);
int cNow=1;
for (int i=1;i<=n;i++){
while (!Q.empty()){
Node tmp=Q.top();
if (i>tmp.R){Ans[tmp.n]=tmp.v;Q.pop();}else
if (tmp.v>=a[i]){//a[i]==0
Q.pop();
Q.push(Node(tmp.R,tmp.v%a[i],tmp.n));
}else break;
}
while (cNow<=m && c[cNow].L==i){
Q.push(Node(c[cNow].R,a[i],c[cNow].n));
cNow++;
}
}
while (!Q.empty()){
Node tmp=Q.top();Q.pop();
Ans[tmp.n]=tmp.v;
}
for (int i=1;i<=m;i++)printf("%d\n",Ans[i]);
}
int main()
{
//freopen("1.txt","r",stdin);
int Case;scanf("%d",&Case);
while (Case--)work();
return 0;
}
題解二:對於詢問i,每次尋找值小於等於當前值的最近位置pos,不斷取模,直到pos>R或者找不到這樣的值爲止。
對於尋找,用線段樹,區間[L,R]表示a(i)的值爲[L,R]時的最小位置。由於可能位置在L之前,所以我們可以對詢問按左邊界L從小到大排序。枚舉位置(i-1)->i時,除去a(i)在線段樹中的值,可以用下一個值爲a(i)的點覆蓋。
處理時需要先對A離散化。複雜度爲O(nlgnlgn)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int N=1e5+10;
struct Sec{
int L,R,n;
Sec(){}
Sec(int L,int R,int n):L(L),R(R),n(n){}
}c[N];
int a[N],n,m;
int d[N],dn;
int T[N<<2];
int Ans[N];
int nxt[N],Head[N];
bool cmp(Sec i,Sec j){return i.L<j.L;}
void WZJ()
{
for (int i=1;i<=n;i++)d[i]=a[i];
sort(d+1,d+n+1);
dn=unique(d+1,d+n+1)-(d+1);
for (int i=1;i<=n;i++)
a[i]=lower_bound(d+1,d+dn+1,a[i])-d;
}
void Insert(int p,int L,int R,int pos,int v)
{
if (L==R){T[p]=v;return ;}
int mid=(L+R)>>1;
if (pos<=mid)Insert(p<<1,L,mid,pos,v);
else Insert(p+p+1,mid+1,R,pos,v);
T[p]=min(T[p+p],T[p+p+1]);
}
int Find(int p,int L,int R,int v)
{
if (R<=v)return T[p];
int mid=(L+R)>>1;
if (v<=mid)return Find(p+p,L,mid,v);
else return min(T[p+p],Find(p+p+1,mid+1,R,v));
}
void Look(int p,int L,int R)
{
printf("%d %d %d\n",L,R,T[p]);
if (L==R)return ;
int mid=(L+R)>>1;
Look(p+p,L,mid);
Look(p+p+1,mid+1,R);
}
void work()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)scanf("%d",&a[i]);
WZJ();
memset(Head,0,sizeof Head);
for (int i=n;i>=1;i--){
nxt[i]=Head[a[i]];
Head[a[i]]=i;
}
for (int i=1;i<(n<<2);i++)T[i]=dn+1;
for (int i=1;i<=dn;i++)
Insert(1,1,dn,a[Head[i]],Head[i]);
scanf("%d",&m);
for (int i=1;i<=m;i++)scanf("%d%d",&c[i].L,&c[i].R),c[i].n=i;
sort(c+1,c+m+1,cmp);
int cNow=1;
for (int i=1;i<=n;i++){
if (nxt[i]==0)Insert(1,1,dn,a[i],n+1);
else Insert(1,1,dn,a[i],nxt[i]);
for (;cNow<=m && c[cNow].L==i;cNow++){
int v=d[a[i]],R=c[cNow].R;
while (1){
int tmp=upper_bound(d+1,d+dn+1,v)-d-1;
if (tmp==0) break;
int pos=Find(1,1,dn,tmp);
if (pos>R)break;
v=v%d[a[pos]];
}
Ans[c[cNow].n]=v;
}
}
for (int i=1;i<=m;i++)printf("%d\n",Ans[i]);
}
int main()
{
//freopen("1.txt","r",stdin);
int Case;scanf("%d",&Case);
while (Case--)work();
return 0;
}