HDU 4777 思维 + 树状数组

HDU 4777
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4777
题意:
一些数,一些询问。
询问一段区间内,和其他数都互质的数有几个。
注意1和所有数包括1本身互质。
思路:
基本参照http://m.blog.csdn.net/blog/u011663071/39059483
首先一个朴素的想法是暴力。
然后就想到应该离线处理所有的询问。关键是对于一个询问,怎么知道在这个区间内一个数有没有对答案做出贡献。模拟赛的时候想到这里也就想不下去了,因为感觉可能和质因数分解有关,而质因数分解的时候又想到容斥原理那边瞬间感觉脑袋爆炸……
其实只要统计一个数的有效区间就可以,具体就是和他左边最近最大公约数不为1 — 右边最大公约数不为1的区间……
统计的话比较巧妙,用一个访问数组统计质因子出现与否与出现位置就可以找到最靠近的左边在哪,同理逆向的过程可以找到右边在哪。然后用维护区间的方式更新区间和就行,这里采用树状数组。
树状数组维护的时候有小技巧,设区间为[l,r],l以后的加1,r以后的减1。 如果一个数遍历过不再使用了,它的右区间r以后都加1,这样可以避免输出答案时少1(假设询问的区间[l2,r2],此次去除的区间[l1,r1],l2 > l1 && l1 < r1的话,就会出现这种情况)。当然你也可以l以后的减1,r以后的加1(相当于之前更新过程作废),这样也好理解一点。
源码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
const int MAXN = 200000 + 5;
int n, m;
struct D
{
    int u, v;
    int mark;
}d[MAXN];
bool cmp(D a, D b){return a.u < b.u;}
int ans[MAXN];
int data[MAXN];
int prime[MAXN], cnt;
int vis[MAXN];
vector<int>p[MAXN];
void init()
{
    cnt = 0;
    memset(vis, 0, sizeof(vis));
    for(int i = 2 ; i < MAXN ; i++){
        if(vis[i] == 0){
            prime[cnt++] = i;
            for(int j = i ; j < MAXN ; j += i)  vis[i] = 1;
        }
    }
    p[0].clear(), p[1].clear();
    for(int j = 0 ; j < cnt ; j++){
        for(int i = prime[j] ; i < MAXN ; i += prime[j])
            p[i].push_back(prime[j]);
    }
}
int tree[MAXN], li[MAXN], ri[MAXN];
int lowbit(int a)
{
    return a & (-a);
}
void updata(int pos, int val)
{
    while(pos <= n){
        tree[pos] += val;
        pos += lowbit(pos);
    }
}
int sum(int pos)
{
    int res = 0;
    while(pos > 0){
        res += tree[pos];
        pos -= lowbit(pos);
    }
    return res;
}
vector<int>vc[MAXN];
void check_tree()
{
    for(int i = 0 ; i <= n ; i++)
        printf("%d ", sum(i));
    printf("\n");
}
int main()
{
    init();
    while(scanf("%d%d", &n, &m) != EOF && n + m){
        for(int i = 1 ; i <= n ; i++)   scanf("%d", &data[i]);
        for(int i = 0 ; i < m ; i++)    scanf("%d%d", &d[i].u, &d[i].v), d[i].mark = i;
        sort(d, d + m, cmp);
        for(int i = 0 ; i <= n ; i++)   tree[i] = li[i] = ri[i] = 0, vc[i].clear();
        memset(vis, 0, sizeof(vis));
        for(int i = 1 ; i <= n ; i++){
            for(int j = 0 ; j < (int)p[data[i]].size() ; j++){
                int mark = p[data[i]][j];
                if(vis[mark] == 0){
                    vis[mark] = i;  continue;
                }
                int a = vis[mark];
                if(ri[a] == 0)  ri[a] = i;
                if(li[i] < a)   li[i] = a;
                vis[mark] = i;
            }
            vc[li[i]].push_back(i);
        }

        int cur = 0;
        for(int i = 0 ; i <= n ; i++){
// printf("in op %d\n", i);
            while(d[cur].u == i && cur < m){
                ans[d[cur].mark] = sum(d[cur].v) - sum(d[cur].u - 1);
                cur++;
            }
            if(cur >= m)    break;
            for(int j = 0 ; j < (int)vc[i].size() ; j++){
                updata(vc[i][j], 1);
                if(ri[vc[i][j]] != 0)   updata(ri[vc[i][j]], -1);
// printf("vc = %d, check tree ", vc[i][j]);
// printf("ri = %d\n", ri[vc[i][j]]);
// check_tree();
            }
            if(ri[i] != 0)  updata(ri[i], 1);
// printf("check tree\n");
// check_tree();
        }
        for(int i = 0 ; i < m ; i++)
            printf("%d\n", ans[i]);
    }
    return 0;
}

你可能感兴趣的:(HDU 4777 思维 + 树状数组)