2016 Multi-University Training Contest 1----解题报告

2016 Multi-University Training Contest 1----解题报告

1.HDOJ 5726 GCD

        比赛做这道题的时候,没有做出来,想到解法写不出来,唉,悲哀了。这道题看了标程的解法,感觉写的太简单了,没看懂,所以搜了一下有没有用线段树的大神,结果还真找到了,谢谢just_sort提供的思路,我一开始确实想的是,第一个输出用线段树的区间GCD,然后第二个输出通过暴力打表的方式,可是真的很暴力,完全就是超时的写法。

         这里暴力打表有一个概念就是,当我在计算2 4 6 7这组数据的时候,4 6和2 4 6的GCD都是2,那么当7和4 6, 2 4 6求GCD和的时候,实际上就是和2就求GCD和,结果为1,如果这里7之前GCD和为2的有n组,那么我们可以断定GCD和为1的组数肯定会增加n组,代码里面虽然使用了双重for循环,但是其实,第二个for循环用来遍历map里面记录的GCD和,而且我们可以知道数据量一旦大了,GCD和很多时候都是1了,所有这个map并不大。

        代码如下:

const int MAXN = 1000010;
int sum[MAXN << 2];
int a[MAXN];
int m = 1;

/线段树模板//
void PushUP(int root)
{
    sum[root] = __gcd(sum[root<<1], sum[root<<1|1]);
}

void build(int root, int left, int right)
{
    if (left == right)
    {
        sum[root] = a[m++];
        return;
    }

    int middle = (left + right) >> 1;
    build(root << 1, left, middle);
    build(root << 1|1, middle + 1, right);
    PushUP(root);
}

int Query(int L, int R, int root, int left, int right)
{
    if (L <= left && right <= R)
    {
        return sum[root];
    }

    int m = (left + right) >> 1;
    int ret = 0;
    if (L <= m)
        ret = __gcd(ret, Query(L, R, root <<  1, left, m));
    if (R > m)
        ret = __gcd(ret, Query(L, R, root << 1|1, m + 1, right));
    return ret;
}
///以上是线段树模板/
int n;
map<LL, LL>ans;     //保存结果
map<LL, LL>mp1;     //记录部分GCD和的组数
map<LL, LL>mp2;     //中间变量

int main()
{
#ifdef LOCAL
    ///freopen("in.txt", "r", stdin);
    ///freopen("out.txt", "w", stdout);
#endif // LOCAL
    int T;
    scanf("%d", &T);
    int Ca = 1;
    while(T--)
    {
        scanf("%d", &n);
        ans.clear();
        mp1.clear();
        mp2.clear();
        m = 1;
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        build(1, 1, n);
        mp1[a[1]]++;
        ans[a[1]]++;
        for(int i = 2; i <= n; ++i)
        {
            LL now = a[i];
            mp2[now]++;
            ans[now]++;
            for(auto it = mp1.begin(); it != mp1.end(); it++)   //扫描确定新的gcd和
            {
                int next = __gcd(now, it->first);
                ans[next] += it->second;     //这里代表就是上面说的前面有n组,只要之前的GCD和相等那么,他与当前数字的GCD和的组合只需要加上n即可
                mp2[next] += it->second;     
            }
            mp1.clear();
            mp1 = mp2;     //复制
            mp2.clear();
        }
        int q;
        printf("Case #%d:\n", Ca++);
        scanf("%d", &q);
        while(q--)
        {
            int l, r;
            scanf("%d%d", &l, &r);
            LL temp = Query(l, r, 1, 1, n);
            printf("%I64d %I64d\n", temp, ans[temp]);
        }
    }
    return 0;
}



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gscsdlz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值