码迷,mamicode.com
首页 > 其他好文 > 详细

扫描线法

时间:2015-08-11 21:17:12      阅读:122      评论:0      收藏:0      [点我收藏+]

标签:

SD集训divisors

题目大意:给定一个1~n的排列p,求l<=a,b<=r中pa%pb=0的(a,b)数对个数。

思路:离线操作。对于1~n所有数在n以内的约数个数n/1+n/2+n/3+...+n/n=nlogn,那么我们可以保存下所有的这样成倍数的关系,按左端点排序,然后暴力插入。对于查询也是离线的,按左端点排序,对于关系的左边已经在当前左端点左边的就删掉,然后对于右边的统计相应的和就可以了(用树状数组维护)。

用到的排序和扫描线的思想。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxnode 200005
using namespace std;
struct use{
    int l,r,po;
}ask[maxnode]={0},bei[4000005];
int ci[maxnode]={0},pos[maxnode]={0},ai[maxnode]={0},n,ans[maxnode]={0};
int lowbit(int x){return x&-x;}
int cmp(const use &x,const use &y){return x.l<y.l;}
void add(int x,int y){for (;x<=n;x+=lowbit(x)) ci[x]+=y;}
void get(int x,int y){for (;y;y-=lowbit(y)) ans[x]+=ci[y];}
int main()
{
    freopen("divisors.in","r",stdin);
    freopen("divisors.out","w",stdout);
    
    int m,i,j,tot=0;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i){scanf("%d",&ai[i]);pos[ai[i]]=i;}
    for (i=1;i<=n;++i)
      for (j=i;j<=n;j+=i){bei[++tot].l=min(pos[i],pos[j]);bei[tot].r=max(pos[i],pos[j]);}
    sort(bei+1,bei+tot+1,cmp);
    for (i=1;i<=tot;++i) add(bei[i].r,1);
    for (i=1;i<=m;++i) {scanf("%d%d",&ask[i].l,&ask[i].r);ask[i].po=i;}
    sort(ask+1,ask+m+1,cmp);j=1;
    for (i=1;i<=m;++i)
    {
        while(j<=tot&&bei[j].l<ask[i].l)
        {
            add(bei[j].r,-1);++j;
        }
        get(ask[i].po,ask[i].r);
    }
    for (i=1;i<=m;++i) printf("%d\n",ans[i]);
}
View Code

 

扫描线法

标签:

原文地址:http://www.cnblogs.com/Rivendell/p/4722190.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!