多校hdu5726 线段树+预处理

第一问是没有修改的线段树,第二问暴力预处理,因为gcd的结果不会很多

在预处理阶段需要把每个区间的gcd相等的数量储存起来(用map容器),在一个序列例如:12467,枚举左区间L直到n此处时间为O(n),l=1时寻找右区间随着r的增大gcd单调不增,在某个区域内gcd相等想到用二分查找时间是O(logn),再加上线段树查找用到时间O(logn),总时间是O(n*logn*longn)会超时,所以需要更优。

解决办法:左区间固定时利用线段树找到gcd减小的区间,从l到gcd减小的区间gcd相等。

#include<map>
#include<stdio.h>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#define  LL long long
const int N = 4e5 + 10;
int T, n, m, g[N], a[N], l, r, q, cas = 0;
map<int, LL> M;
int gcd(int x, int y) { return x%y ? gcd(y, x%y) : y; }
void build(int x, int l, int r)
{
    if (l == r) scanf("%d", &g[x]), a[l] = g[x];
    else
    {
        int mid = l + r >> 1;
        build(x << 1, l, mid);
        build(x << 1 | 1, mid + 1, r);
        g[x] = gcd(g[x << 1], g[x << 1 | 1]);
    }
}

int get(int x, int l, int r, int ll, int rr)
{
    if (ll <= l&&r <= rr) return g[x];
    int mid = l + r >> 1;
    int x1 = 0, x2 = 0;
    if (ll <= mid) x1 = get(x << 1, l, mid, ll, rr);
    if (rr > mid) x2 = get(x << 1 | 1, mid + 1, r, ll, rr);
    return gcd(min(x1, x2), max(x1, x2));
}

bool find(int x, int l, int r, int ll, int rr, int u, int &v)
{
    if (ll <= l && r <= rr)
    {
        if (gcd(v, g[x]) < u)
        {
            if (l == r)
            {
                q = l; return true;
            }
            else
            {
                int mid = l + r >> 1;
                if (find(x<<1, l, mid, ll, rr, u, v)) return true;
                if (find(x<<1|1, mid + 1, r, ll, rr, u, v)) return true;
            }
        }
        else { v = gcd(v, g[x]); return false; }
    }
    else
    {
        int mid = l + r >> 1;
        if (ll <= mid&&find(x<<1, l, mid, ll, rr, u, v)) return true;
        if (rr > mid&& find(x<<1|1, mid + 1, r, ll, rr, u, v)) return true;
        return false;
    }
}

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        M.clear();
        scanf("%d", &n);
        build(1, 1, n);
        for (int i = 1, j, k; i <= n; i++)
        {
            int kk = get(1, 1, n, i, n);
            for (k = i, j = a[i]; k <= n;)
            {
                if (kk == j) { M[j] += n - k + 1; break; }
                int gg = a[i];
                find(1, 1, n, i, n, j, gg);
                M[j] += q - k;
                k = q;    j = gcd(a[q], j);
            }
        }
        scanf("%d", &m);
        printf("Case #%d:\n", ++cas);
        while (m--)
        {
            scanf("%d%d", &l, &r);
            int x = get(1, 1, n, l, r);
            printf("%d %lld\n", x, M[x]);
        }
    }
    return 0;
}

  

posted on 2016-07-26 08:25  远搏  阅读(363)  评论(0)    收藏  举报

导航