通過這道題目還是學到了不少東西的,當時剛拿到這個題目的時候時間已經不多了,因爲前面有個C坑到了,看了個大概,然後就往線段樹和樹狀數組方面去想了,對於gcd的區間求一個前綴,再搞一個後綴 瞎弄弄,後來發現錯了,題目求的是區間個數。。。又浪費了一段時間,然後回頭再看看,大致就想到了暴力枚舉,n是10^5,大不了離線先暴力的高出答案,想到一般發現 若假定一個詢問輸入的數 爲 X,那麼另一個 能夠 y 通過gcd(x,y)得到的另一個數的 答案就是它自身的答案數再加上當前x的答案數,這樣就有遞推了,於是趕緊寫了一下,結果T了!,沒有做出來,補題也拖了很久。。。後來發現 自己的錯誤之處,其實對於G(l,r),G(l,r+1)後者肯定比前者更小,這句話在一個博客中也看到了,自己當時 順序沒寫好。。。後來看到一個博客推薦的另一個 博客,發現了一個很好的解決辦法
http://hplonline20100103.blog.163.com/blog/static/1361364342010040044244/
是以POJ3368爲例子,然後看的半懂不懂的 ,磕磕碰碰的去把POJ3368給A了,又做了這道題目也過了,後來看CF的官方,題解發現了另一種解法,
http://blog.csdn.net/zearot/article/details/40180791 這篇博客就是第二種解法
枚舉的是區間,然後利用兩個map 類似於滾動數組一樣進行傳遞再利用,保存在另一個map裏,這個方法最好理解也非常簡潔,參照他的 敲了一發 ,順便紀念一下!
map<int ,ll> res;
map<int ,int >now,nex;
map<int ,int > ::iterator it;
int n;
int aa[100000 + 55];
int q;
void init() {
memset(aa,0,sizeof(aa));
}
bool input() {
while(cin>>n) {
for(int i=0;i<n;i++)scanf("%d",&aa[i]);
return false;
}
return true;
}
int Gcd(int a,int b) {
return b == 0?a:Gcd(b,a%b);
}
void cal() {
for(int i=0;i<n;i++) {
nex.clear();
for(it=now.begin();it!=now.end();it++)
nex[Gcd(it->first,aa[i])] += it->second;
nex[aa[i]]++;
swap(nex,now);
for(it = now.begin();it != now.end();it++)
res[it->first] += it->second;
}
int q;
cin>>q;
while(q--) {
int x;
scanf("%d",&x);
printf("%I64d\n",res[x]);
}
}
void output() {
}
int main() {
while(true) {
init();
if(input())return 0;
cal();
output();
}
return 0;
}