CF 475D CGCDSSQ 解题报告(DP)

56 篇文章 0 订阅
11 篇文章 0 订阅

D. CGCDSSQ
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Given a sequence of integers a1, ..., an and q queries x1, ..., xq on it. For each query xi you have to count the number of pairs(l, r) such that 1 ≤ l ≤ r ≤ n and gcd(al, al + 1, ..., ar) = xi.

 is a greatest common divisor of v1, v2, ..., vn, that is equal to a largest positive integer that divides all vi.

Input

The first line of the input contains integer n, (1 ≤ n ≤ 105), denoting the length of the sequence. The next line contains nspace separated integers a1, ..., an, (1 ≤ ai ≤ 109).

The third line of the input contains integer q, (1 ≤ q ≤ 3 × 105), denoting the number of queries. Then follows q lines, each contain an integer xi, (1 ≤ xi ≤ 109).

Output

For each query print the result in a separate line.

Sample test(s)
input
3
2 6 3
5
1
2
3
4
6
output
1
2
2
0
1
input
7
10 20 3 15 1000 60 16
10
1
2
3
4
5
6
10
20
60
1000
output
14
0
2
2
2
0
2
2
1
1

    解题报告: 昨晚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");
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值