atcoder ARC 068 E(树状数组+思维)

题目链接:点击打开链接


题目大意:n种纪念品能在第l个车站到第r个车站能买到,一共有m个车站,问你一次分别隔1个,2个……m个车站分别能买到几种纪念品

题目思路:刚开始还以为直接暴力就好,结果第七个样例就凉了,结束以后看了学长代码没看懂,问了学长好久,想了两个小时才搞懂..太菜了,回归正题。

这道题是用vector把区间长度相同的车站序号放在一起(非常机智的做法),然后开始从1到m也就是车站间隔进行求解,如果纪念品能买的车站区间比车站间隔要大,那很明显这种纪念品肯定可以被买,也就是后面的n。然后如果有跟车站间隔相同的车站区间纪念品,那就把能买到这种纪念品的地方都+1,然后从i开始每次跳i把数字加起来就行了,因为车站间隔和纪念品车站区间相同,所以这么跳的话,肯定每个地方加的数字对应的纪念品都是独一无二的纪念品。


以下是代码:

#include
using namespace std;
#define inf 0x3f3f3f3f
#define MAXN 300005
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
int n,m,sum[MAXN];
struct node{
    int l,r;
}a[MAXN];
vectorv[MAXN];
int lowbit(int x){
    return x&-x;
}
void add(int x,int y){
    while(x<=m){
        sum[x]+=y;
        x+=lowbit(x);
    }
}
int query(int x){
    int ans=0;
    while(x){
        ans+=sum[x];
        x-=lowbit(x);
    }
    return ans;
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        memset(sum,0,sizeof(sum));
        rep(i,1,m){
            v[i].clear();
        }
        rep(i,1,n){
            scanf("%d%d",&a[i].l,&a[i].r);
            int temp=a[i].r-a[i].l+1;
            v[temp].push_back(i);
        }
        rep(i,1,m){
            int ans=0,len=v[i].size();
            rep(j,0,len-1){
                int pos=v[i][j];
                add(a[pos].l,1);
                add(a[pos].r+1,-1);
            }
            for(int j=i;j<=m;j+=i){
                ans+=query(j);
            }
            n-=len;
            printf("%d\n",ans+n);
        }
    }
    return 0;
}

你可能感兴趣的:(树状数组)