• hdu 5726 GCD 倍增+ 二分


    题目链接

    给n个数, 定义一个运算f[l,r] = gcd(al, al+1,....ar)。 然后给你m个询问, 每次询问给出l, r。 求出f[l, r]的值以及有多少对l', r' 使得f[l, r] = f[l', r']。

    第一个很简单, 用倍增的思想就可以了。 

    然后是第二个, 我们枚举每一个左端点i, 显然f[i, j]是只降不增的。 那么我们可以二分找到所有使得f[i, j]下降的值j。 因为gcd每次至少变为原来的二分之一, 而ai最大为1e9. 所以最多只有log2(1e9)个这样的点。 我们都找出来然后放到map里就可以了。 具体看代码

    #include <bits/stdc++.h>
    
    using namespace std;
    #define ll long longint n;
    const int maxn = 1e5+5;
    int a[maxn], f[maxn][20], mm[maxn];
    map <int, ll> mp;
    int gcd(int a, int b)
    {
        return b?gcd(b, a%b):a;
    }
    void initrmq()
    {
        mm[0] = -1;
        for(int i = 1; i <= n; i++) {
            mm[i] = ((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
        }
        for(int j = 1; j < 17; j++) {
            for(int i = 1; i + (1<<j)-1 <= n; i++) {
                f[i][j] = gcd(f[i][j-1], f[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int query(int l, int r)
    {
        int k = mm[r-l+1];
        return gcd(f[l][k], f[r-(1<<k)+1][k]);
    }
    void pre()
    {
        for(int i = 1; i <= n; i++) {
            int g = f[i][0];
            int L = i, tmp;
            while(L <= n) {
                int l = L, r = n;
                while(l <= r) {
                    int mid = l+r>>1;
                    if(query(i, mid) == g) {
                        tmp = mid;
                        l = mid+1;
                    } else {
                        r = mid-1;
                    }
                }
                mp[g] += (tmp-L+1);
                L = tmp+1;
                g = gcd(g, f[L][0]);
            }
        }
    }
    int main()
    {
        int t, m, l, r;
        cin>>t;
        for(int casee = 1; casee <= t; casee++) {
            cin>>n;
            for(int i = 1; i <= n; i++) {
                scanf("%d", &a[i]);
                f[i][0] = a[i];
            }
            mp.clear();
            initrmq();
            pre();
            cin>>m;
            printf("Case #%d:
    ", casee);
            for(int i = 0; i < m; i++) {
                scanf("%d%d", &l, &r);
                int ans = query(l, r);
                printf("%d %lld
    ", ans, mp[ans]);
            }
        }
    }
  • 相关阅读:
    C# 读取计算机CPU,HDD信息
    实现多线程下载文件
    安装SharePoint Server的主机重命名
    SharePoint 2010: Change welcome page on PowerShell
    FW: Solving SharePoint Server -503. The service is unavailable, After installation
    XDocument 使用
    ListBox 控件单击事件
    设计Popup Window
    Listbox Binding ItemsSource
    ListBox item Sort
  • 原文地址:https://www.cnblogs.com/yohaha/p/5689514.html
Copyright © 2020-2023  润新知