hdu4777 Rabbit Kingdom 树状数组+区间操作+素数打表

题目大意:给N个数,有M个查询,问区间[L,R]之间有多少个数与这个区间内的其他数都互质。

思路:dp显然很难搞,既然是求区间就试试每一个数最大可以对答案产生贡献的区间,即预处理出一个数在(lp,rp)内始终与其他数互质的最大区间

则lp和rp分别为左边和右边第一个与他不互质的数的位置

处理的时候素数打表然后从左到右始终更新对于某一个素因子出现的最右的位置然后更新l,r,可以做到

然后就是把查询按l从小到大排序,这样的话每处理一个新的查询,对于在这个查询的 l 左边的数就可以不用考虑了

然后我们假设先处理左边界为1的查询,很明显如果我们把lp为0的所有位置pos都加上1同时把pos对应的rp减去1,用rp[pos]-1表示

然后用树状数组求这次查询的区间[l,r]的和即为答案

左边界为1的处理出来了,很容易就想到左边界为2,3.....n的处理方式,即与上诉类似

还有一个问题就是当处理的左边界不断增大有则左边界的左边的数会不断被舍去此时就要注意消除掉被舍去的数对于树状数组的影响,设每一步被舍弃的位置为i,则需要让树状数组的rp[i]位置加上1

附代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
#define PF(x) cout << "debug: " << x << " ";
#define EL cout << endl;
#define PC(x) puts(x);
typedef long long ll;
#define CLR(x, v) sizeof (x, v, sizeof(x))
using namespace std;
const int INF = 0x5f5f5f5f;
const int  N= 1000050;
const int maxn=200000+10;
int n,m,r[maxn],l[maxn],a[maxn],tree[maxn],pri[maxn],mark[maxn];
vector<int>p[maxn],vt[maxn];
struct st
{
    int l,r,id;

} q[maxn];
void prime()
{
   // int sr = sqrt(200000+5+0.5);
    for(int i = 2; i <= 200000; i++)
        if(!pri[i]){
            p[i].push_back(i);
            for(int j = i+i; j <= 200000; j+=i)
            {
                pri[j]=1;
                p[j].push_back(i);
            }
        }
}
void pre()
{
    memset(mark,0,sizeof(mark));
    for(int i = 0; i <= n; i++)
    {
        tree[i]=0;
        vt[i].clear();
        l[i]=0;
        r[i]=n+1;
    }
    for(int i= 1; i <= n; i++)
    {
        int lv=p[a[i]].size();
        //cout<<lv<<" "<<a[i]<<" "<<i<<endl;
        for(int j= 0; j < lv; j++)
        {
            int x = p[a[i]][j];
            if(!mark[x])
            {
                mark[x] = i;
                continue;
            }
            if(r[mark[x]] == n+1)
                r[mark[x]] = i;
            l[i] = max(l[i],mark[x]);
            mark[x]=i;
        }
        vt[l[i]].push_back(i);
    }
   // for(int i= 1;i <= n;i++)
       // cout<<l[i]<<" "<<r[i]<<endl;
}
bool cmp(st x,st y)
{
    return x.l<y.l;
}
void add(int k , int num)
{
    while(k <= n)
    {
        tree[k] += num;
        k += k&(-k);
    }
}
int Sum(int k)
{
    int sum = 0;
    while(k > 0)
    {
        sum += tree[k];
        k -= k&(-k);
    }
    return sum;
}
int main()
{
   // freopen("in.txt","r",stdin);
    prime();
    while(~scanf("%d%d",&n,&m))
    {
        //cout<<"r"<<endl;
        if(n == m&&m == 0)
            break;
        int i,j;
        for(i = 1; i <= n; i++)
            scanf("%d",&a[i]);
        for(i = 1; i <= m; i++)
        {
            scanf("%d%d",&q[i].l,&q[i].r);
           // cout<<q[i].l<<" "<<q[i].r<<endl;
            q[i].id=i;
        }
        pre();
        sort(q,q+1+n,cmp);
        int cnt=1,ans[maxn];
        for(int i = 0; i <= n; i++)
        {
            while(q[cnt].l == i)
            {
                ans[q[cnt].id] = Sum(q[cnt].r)-Sum(q[cnt].l-1);
                //cout<<ans[q[cnt].id]<<" "<<q[cnt].id<<endl;
                cnt++;

            }
            int x=vt[i].size();
            for(j = 0;j < x;j++){
                int v=vt[i][j];
                add(v,1);
                add(r[v],-1);
            }
            if(r[i]!=n+1)
                add(r[i],1);

            }
        for(i = 1;i <= m;i++)
            cout<<ans[i]<<endl;

    }
    return 0;
}
时间: 2024-11-05 10:17:13

hdu4777 Rabbit Kingdom 树状数组+区间操作+素数打表的相关文章

hdu 4777 Rabbit Kingdom(树状数组)

题目链接:hdu 4777 Rabbit Kingdom 题目大意:一个兔子王国,有N只兔子,每只兔子有一个重量,如果两只兔子的重量不互质,那么就会干架,现在国王想将l r之间的兔子关进监狱,它想知道会有多少只兔子不会和别的兔子干架. 解题思路:预处理出每只兔子的L,R表示向左和向右最近会与该兔子发生冲突的兔子,预处理的时候只要将每只兔子的重量分解成质因子后遍历两遍. 对于询问,将询问按照右区间排序,碰到i,则L位置+1,碰到R,则i位置+1,L位置-1.(如果L ≤ l && r ≤ R

hdu 1754 I Hate It(树状数组区间求最值)2007省赛集训队练习赛(6)_linle专场

题意: 输入一行数字,查询第i个数到第j个数之间的最大值.可以修改其中的某个数的值. 输入: 包含多组输入数据. 每组输入首行两个整数n,m.表示共有n个数,m次操作. 接下来一行包含n个整数. 接下来m行,每行包含一个字母s,两个整数a,b. 当s为’Q’,表示查询第a个数到第b个数之间的最大值. 当s为’U’,表示将第a个数更改为b. 输出: 每次查询输出一个结果,每次输出占一行. 题解: 点修改区间求最值,可以用树状数组模板. 具体见代码—— 1 #include <cstdio> 2

树状数组区间更新

在今天的文章开始之前,给大家提一个建议,由于线段树和树状数组这两个结构的分析有很多联系,因此,建议没有看前几篇文章的朋友一定需要了解一下前面的内容.链接如下: 线段树+RMQ问题第二弹 线段树第二弹(区间更新) 树状数组(Binary Indexed Tree,BIT) 上篇文章我们讨论了树状数组的基本结构以及它最擅长的两个功能:单点更新和区间求和,今天,我们来接着上一篇文章的内容继续深入研究.上篇文章我们是将树状数组和线段树进行对比讲解的,既然线段树的章节我们介绍了区间求和.区间最值.单点更新

【BZOJ】1012: [JSOI2008]最大数maxnumber(树状数组+区间最值)

http://www.lydsy.com/JudgeOnline/problem.php?id=1012 树状数组原来我只懂得sum和add的操作,今天才知道可以有求区间最值的操作,我学习了一下写了个,1a了. 区间最值其实和区间求和差不多,就是将sum数组的含义转移到max,然后通过特定的区间更新max. 在区间求和中,当我们维护max[i]的时候,要找到它前面所有的max[j]来更新,在这里因为是树状数组,所以可以降成一个log级,画图可知,max[i]需要的max只有max[i-2^0],

【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询

题目描述 给出一棵n个节点的树,每一个节点开始有一个互不相同的颜色,初始根节点为1. 定义一次感染为:将指定的一个节点到根的链上的所有节点染成一种新的颜色,代价为这条链上不同颜色的数目. 现有m次操作,每次为一下三种之一: RELEASE x:对x执行一次感染: RECENTER x:把根节点改为x,并对原来的根节点执行一次感染: REQUEST x:询问x子树中所有节点感染代价的平均值. 输入 输入的第一行包含两个整数n和m,分别代表局域网中计算机的数量,以及操作和询问的总数.接下来n-1行,

POJ 1195-Mobile phones(二维树状数组-区间更新区间查询)

Mobile phones Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 17661   Accepted: 8173 Description Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows. The area is divided into squares. The

【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. 输入 第一行N,M接下来M行,每行形如1 a b c或2 a b c 输出 输出每个询问的结果 样例输入 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 样例输出 1 2 1 题解 整体二分+树状数组区间修改 当年naive的树套树题解 前两天由于要

hdu 1116 敌兵布阵(树状数组区间求最值)

题意: 给出一行数字,然后可以修改其中第i个数字,并且可以询问第i至第j个数字的和(i <= j). 输入: 首行输入一个t,表示共有t组数据. 接下来每行首行输入一个整数n,表示共有n个数字. 接下来每行首先输入一个字符串,如果是”Add”,接下来是两个整数a,b,表示给第i个数增加b.如果是”Query”,接下来是两个整数a,b,表示查询从第i个数到第j个数之间的和.如果是”End”,表示这组数据结束. 输出: 每组数据首先输出”Case X: ”,其中X表示第x组数. 每次查询,输出计算结

HRBUST 1161 树状数组区间更新求和

Leyni Time Limit: 3000 MS Memory Limit: 65536 K Total Submit: 267(64 users) Total Accepted: 82(57 users) Rating: Special Judge: No Description Leyni被人掳走,身在水深火热之中... 小奈叶为了拯救Leyni,独自一人前往森林深处从静竹手中夺回昏迷中的Leyni. 历经千辛万苦,小奈叶救出了Leyni,但是静竹为此极为恼怒,决定对他们发起最强烈的进攻.