题意:
给你n个数,然后询问m次,求出每次询问的区间内有多少个数是与该区间内其它数是互质的。
思路:
这题真的是做好久,这题的离线思想的确是犀利!一开始对铭神跟我讲的离线思想不太懂,后来看了qian99的解题报告才弄懂。
http://blog.csdn.net/qian99/article/details/14768235
数组lt[i], rt[i]。表示第i个数,左右两边距离i的最近的,与i不互质的下标。
具体看上面那份题解,我就不班门弄斧了。
总结:
主要卡在处理出lt, rt这两个数组。一开始是从2到sqrt(n)来枚举,把一个数的所有因子全部求出来,复杂度O(nsqrt(n)),TLE。
后来杰神教了O(n)筛选素数大法,以及把每个数的质因子求出来。根据唯一分解定理,每个大于1的自然数都可以写成质数的乘积。因此,两个数如果不是互质,那么他们至少有一个质因子是相同的。
预处理出每个数的质因子,保存在vecotr里面;再开个数组rd,每次都保存该质因子的最新下标即可。
往右扫一遍,往左扫一遍,lt,rt数组就出来了。
*这题加个快速读入收益很可观= =
code:
/* **********************************************
Created Time: 2014-10-23 19:19:51
File Name : hdu4777.cpp
*********************************************** */
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <cmath>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long LL;
const int MAXN = 200100;
const int INF = 0x3f3f3f3f;
struct PP
{
int l, r, rank;
int ans;
}query[MAXN];
int lt[MAXN], rt[MAXN];
int rd[MAXN], a[MAXN];
int n, m;
bool Cmp(PP a, PP b)
{
return a.r < b.r;
}
bool Cmp2(PP a, PP b)
{
return a.rank < b.rank;
}
//datastruct
int c[MAXN]; //BIT
inline int Lowbit(int x)
{
return x&(-x);
}
int Query(int x)
{
int ret = 0;
while(x > 0)
{
ret += c[x];
x -= Lowbit(x);
}
return ret+c[0];
}
void Update(int x, int val)
{
if(x == 0)
{
c[x] += val;
return ;
}
while(x <= n)
{
c[x] += val;
x += Lowbit(x);
}
}
//
vector <int> ins[MAXN];
void Solve()
{
//priority_queue <TI> myque;
//memset(c, 0, sizeof(c));
for(int i = 0;i <= n+1; i++)
ins[i].clear();
for(int i = 0;i <= n+5; i++)
c[i] = 0;
for(int i = 1, j = 1;i <= n; i++) //i: num; j : query;
{
Update(i, 1);
Update(lt[i], -1);
ins[rt[i]].push_back(i);
//
if(ins[i].size() > 0)
{
for(int z = 0;z < ins[i].size(); z++)
{
int tt = ins[i][z];
Update(tt, -1);
Update(lt[tt], 1);
}
}
//
while(query[j].r == i)
{
PP &e = query[j];
e.ans = Query(e.r) - Query(e.l-1);
j++;
}
}
//cout<<"c[0] = "<<c[0]<<endl;
}
inline int qin()
{
char ch = getchar();
int ret = 0;
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9')
{
ret = ret*10 + ch-'0';
ch = getchar();
}
return ret;
}
const int P = MAXN;
int pri[P], pnum, mindiv[P]; //pri:save prime
bool vis[P];
void get_prime(int tt) {
pnum = 0; vis[1] = 1;
for(int i = 2;i <= tt; i++) {
if(!vis[i]) {
pri[pnum++] = i;
mindiv[i] = i;
}
for(int j = 0;j < pnum; j++) {
LL tmp = (LL)i*pri[j];
if(tmp > tt) break;
vis[i*pri[j]] = 1;
mindiv[i*pri[j]] = pri[j];
if(i%pri[j] == 0) break;
}
}
}
vector <int> pcur[MAXN];
void preDeal(int cur)
{
int a = cur;
if(pcur[cur].size() != 0) return;
while(cur > 1) {
int tmp = mindiv[cur];
pcur[a].push_back(tmp);
while(cur%tmp == 0) cur /= tmp;
}
}
int main()
{
get_prime(MAXN);
while(scanf("%d%d" ,&n, &m) != EOF)
{
if(n == 0 && m == 0) break;
int valmax = 0;
for(int i = 1;i <= MAXN; i++)
pcur[i].clear();
for(int i = 1;i <= n; i++)
{
a[i] = qin();
//scanf("%d", &a[i]);
preDeal(a[i]);
//valmax = max(a[i], valmax);
}
//left
memset(rd, 0, sizeof(rd));
for(int i = 1;i <= n; i++)
{
//update
int pi = 0;
for(int j = 0;j < pcur[a[i]].size(); j++)
{
int tt = pcur[a[i]][j];
pi = max(pi, rd[tt]);
rd[tt] = i;
}
lt[i] = pi;
}
//right
memset(rd, INF, sizeof(rd));
for(int i = n;i >= 1; i--)
{
int pi = n+1;
for(int j = 0;j < pcur[a[i]].size(); j++)
{
int tt = pcur[a[i]][j];
pi = min(pi, rd[tt]);
rd[tt] = i;
}
rt[i] = pi;
}
/*cout<<"test"<<endl;
for(int i = 1;i <= n; i++)
cout<<lt[i]<<" "<<rt[i]<<endl;*/
//query
int l, r;
for(int i = 1;i <= m; i++)
{
l = qin();
r = qin();
//scanf("%d%d" ,&l ,&r);
query[i] = (PP){l ,r, i};
}
sort(query+1, query+m+1, Cmp);
/*cout<<"test"<<endl;
for(int i = 1;i <= m; i++)
cout<<query[i].r<<" ";
puts("");
*/
//
Solve();
sort(query+1, query+m+1, Cmp2);
for(int i = 1;i <= m; i++)
printf("%d\n", query[i].ans);
}
return 0;
}