解题报告: 昨晚CF的D题。当时想枚举起点,然后按照gcd的递减,二分出相同gcd时的最大终点,计算段gcd时用线段树。无奈超时……
队友DP出来了,利用的性质是一样的。一段数长度越长,gcd越小,且严格单调递减。因为int类型在2^31以内,所以可以肯定一个数最多有31个gcd(每次减少至少一个素因子)。我们可以枚举终点,当前的gcd数可以由上一次gcd的结果递推出来。这样问题就解决了。
代码如下(GNU C++0x 提交):
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <iomanip>
#include <cassert>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define ff(i, n) for(int i=0;i<(n);i++)
#define fff(i, n, m) for(int i=(n);i<=(m);i++)
#define dff(i, n, m) for(int i=(n);i>=(m);i--)
#define travel(e, u) for(int e = u, v = vv[u]; e; e = nxt[e], v = vv[e])
#define bit(n) (1LL<<(n))
#define And(a, b) ((a) & (b))
#define Or(a, b) ((a) | (b))
#define Xor(a, b) ((a) ^ (b))
#define clr(a, b) memset((a), b, sizeof(a))
typedef long long LL;
typedef unsigned long long ULL;
void work();
int main()
{
#ifdef ACM
freopen("in.txt", "r", stdin);
#endif // ACM
work();
return 0;
}
void scanf(int & x, char ch = 0)
{
while((ch=getchar()) < '0' || ch > '9');
x = ch - '0';
while((ch=getchar()) >= '0' && ch <= '9') x = x * 10 + ch - '0';
}
/***************************************************************************************/
int gcd(int a, int b)
{
if(a&&b) while(a>b?a=a%b:b=b%a);
return a+b;
}
int a[111111];
int q[333333];
void work()
{
int n, m;
while(scanf("%d", &n) == 1)
{
fff(i, 1, n) scanf(a[i]);
int m;
scanf("%d", &m);
ff(i, m) scanf(q[i]);
map<int, LL> ans;
map<int, int> dp[2];
int now = 0, pre = 1;
fff(i, 1, n)
{
swap(now, pre);
dp[now].clear();
dp[now][a[i]] = 1;
for(auto it : dp[pre])
dp[now][gcd(a[i], it.first)] += it.second;
for(auto it : dp[now])
ans[it.first] += it.second;
}
ff(i, m) if(ans.count(q[i]))
printf("%I64d\n", ans[q[i]]);
else
puts("0");
}
}