HDU 4777 Rabbit Kingdom 区间内区间 and 容斥

 Long long ago, there was an ancient rabbit kingdom in the forest. Every rabbit in this kingdom was not cute but totally pugnacious, so the kingdom was in chaos in season and out of season.
  n rabbits were numbered form 1 to n. All rabbits' weight is an integer. For some unknown reason, two rabbits would fight each other if and only if their weight is NOT co-prime.
  Now the king had arranged the n rabbits in a line ordered by their numbers. The king planned to send some rabbits into prison. He wanted to know that, if he sent all rabbits between the i-th one and the j-th one(including the i-th one and the j-th one) into prison, how many rabbits in the prison would not fight with others.
  Please note that a rabbit would not fight with himself.

Sample Input

3 2
2 1 4
1 2
1 3
6 4
3 6 1 2 5 3
1 3
4 6
4 4
2 6
0 0

Sample Output

2
1
1
3
1
2


 解:

题意:告诉你 a[i]和他不互质的a[i]打架 问区间多少个不打架?

可以想到要用树状数组离线,

1、然后凑出询问形式,问 l,r区间内 有多少点i  满足 L[i]>=l or  R[i]<=r 

2、因为两个是或的关系 考虑容痴求解

3、那么拆分成三次询问,一次询问的两个条件或的满足个数为: 两个条件分别满足之和-两条件同时满足之和

类似于  P(A+B) =P(A)+P(B)-P(AB)

区间内求区间的方法是和 之前有某提求一个树子树独有的颜色个数个数类似

1.对询问的r排序(升)2.对每个区间的r同样升序3.更新4.查询

void run(int x){
    mem(c,0);
    for(int i=1,j=1;i<=m;i++){
        while(seg[x][j].r<=que[i].r and j<=n){
            add(seg[x][j].l,1);
            j+=1;
        }
        ans[x][que[i].ind]=ask(que[i].r)-ask(que[i].l-1);
    }
}

AC代码:

预处理初始的L[i]和R[i]的 pos数组会呗卡常 ,pos要改成一维

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#define ll long long
using namespace std;
const int maxn =2e5+10;
int rd() {int t;scanf("%d",&t);return t;}
#define en '\n'
#define mem(a,b) memset(a,b,sizeof(a))
#define pb push_back
//#define int register int
int a[maxn],n,m,pos[maxn],L[maxn],R[maxn],c[maxn],que_tot,len[maxn],mx;
void build_seg(){
    for(int j=0;j<=mx;j++)pos[j]=0;
    for(int i=1;i<=n;i++){
        int x=a[i];
        for(int j=2;j*j<=x ;j++){
            if(x%j==0){
                L[i]=max(L[i],pos[j]);
                pos[j]=i;
                while((x%j)==0)x/=j;
            }
        }
        if(x!=1){
            L[i]=max(pos[x],L[i]);
            pos[x]=i;
        }
    }
     for(int j=0;j<=mx;j++){
        pos[j]=n+1;
    }
    for(int i=n;i>=1;--i){
        int x=a[i];
        for(int j=2;j*j<=x ;j++){
            if(x%j==0){
               R[i]=min(R[i],pos[j]);
                pos[j]=i;
                while((x%j)==0)x/=j;
            }
        }
        if(x>1){
            R[i]=min(pos[x],R[i]);
            pos[x]=i;
        }
    }
}

struct node{
    int l,r,ind;
}que[maxn],seg[3][maxn];
bool cmp(node a,node b){return a.r<b.r;}
int ans[3][maxn];
void place_seg(){
        for(int i=1;i<=n;i++){
                seg[0][i]=(node){L[i],i,0};
        }
        for(int i=1;i<=n;i++){
                seg[1][i]=(node){i,R[i],0};
        }
        for(int i=1;i<=n;i++){
                seg[2][i]=(node){L[i],R[i],0};
        }
        for(int k=0;k<3;k++){
            sort(seg[k]+1,seg[k]+1+n,cmp);
        }
}
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) (x&(-x))
void add(int x,int val){
    if(x<=0)return ;
    for(;x<maxn-3;x+=lowbit(x)){
        c[x]+=val;
    }
}
signed ask(int x){if(x<=0)return 0;int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
void run(int x){
    mem(c,0);
    for(int i=1,j=1;i<=m;i++){
        while(seg[x][j].r<=que[i].r and j<=n){
            add(seg[x][j].l,1);
            j+=1;
        }
        ans[x][que[i].ind]=ask(que[i].r)-ask(que[i].l-1);
    }
}
signed main() {
    #ifdef swt
    freopen("input2.txt","r",stdin);
    #endif // local
    #define int register int
    while(scanf("%d%d",&n,&m)!=EOF and n+m){
        mx=0;
        for(int i=1;i<=n;i++)a[i]=rd(),L[i]=0,R[i]=n+1,mx=max(mx,a[i]);
        build_seg();
        for(int i=1;i<=m;i++){
            que[i].l=rd();que[i].r=rd();
            que[i].ind=i;len[i]=que[i].r-que[i].l+1;
        }
        sort(que+1,que+1+m,cmp);
        place_seg();
        for(int k=0;k<3;k++){run(k);}
        for(int i=1;i<=m;i++)
        {
            cout<<len[i]-(ans[0][i]+ans[1][i]-ans[2][i])<<en;
        }
    }
        return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值