标签:
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]); }
标签:
原文地址:http://www.cnblogs.com/Rivendell/p/4722190.html