#6164. 「美团 CodeM 初赛 Round A」数列互质
思路 : 对这个题来言,莫队可以 n*根号n 离线处理出各个数出现个的次数 ,同时可以得到每个次数出现的次数 ,
但是还要处理有多少 次数 与ki互质 。根据数列的性质,无论这个区间多长,最长也就是 1 - n这个区间 ,所能产生的
不同的次数 也就是 根号 n 种 例如 长度为28的 数列 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 不同的次数 只有 7种
数列越长了 这个性质体现的越明显, 原因就是 根据他们的出现的次数求个和 就算他们都按 最小的次数出现的话 1 2 3 4...s 求和为 (1+s)*s/2
而这个数又不能超过 区间长度 ,所以 这个 s也就在 根号 长度左右。 所以可以放心的进行暴力求解,有插入删除操作,可以数组模拟链表实现。
#include<bits/stdc++.h> using namespace std; #define maxn 55555 int n,m,cnt[maxn*2],tong[maxn*2],block,ans[maxn],data[maxn]; int nxt[maxn*2],lst[maxn*2],head=0,tail=maxn-1,l,r; struct node { int l,r,k,id; bool operator <(const node &b)const { return l/block==b.l/block?r<b.r:l<b.l; } } a[maxn]; void upda(int rd) { if(rd<=head)return; nxt[rd]=nxt[head]; nxt[head]=rd; lst[rd]=head; lst[nxt[rd]]=rd; } void rmove(int rd) { if(rd<=head)return; nxt[lst[rd]]=nxt[rd]; lst[nxt[rd]]=lst[rd]; } void ins(int x) { if(--tong[cnt[x]]==0)rmove(cnt[x]); if(++tong[++cnt[x]]==1)upda(cnt[x]); } void del(int x) { if(--tong[cnt[x]]==0)rmove(cnt[x]); if(++tong[--cnt[x]]==1)upda(cnt[x]); } int main() { scanf("%d%d",&n,&m); block=sqrt(n); nxt[0]=tail; lst[tail]=0; for(int i=1; i<=n; i++) scanf("%d",&data[i]); for(int i=0; i<m; i++) { a[i].id=i; scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].k); } sort(a,a+m); l=a[0].l; r=a[0].l-1; for(int i=0; i<m; i++) { while(l>a[i].l) ins(data[--l]); while(l<a[i].l) del(data[l++]); while(r>a[i].r) del(data[r--]); while(r<a[i].r) ins(data[++r]); int cp=0; for(int j=nxt[head]; j!=tail; j=nxt[j]) if(__gcd(a[i].k,j)==1) cp+=tong[j]; ans[a[i].id]=cp; } for(int i=0; i<m; i++)printf("%d\n",ans[i]); return 0; }