二维偏序 I query @ The Preliminary Contest for ICPC Asia Xuzhou 2019
I query @ The Preliminary Contest for ICPC Asia Xuzhou 2019
题意
1e5个数1~n,1e5个询问
(
l
,
r
)
(l,r)
(l,r),问
(
l
,
r
)
(l,r)
(l,r)中有几对数
(
i
,
j
)
(i,j)
(i,j)满足
min
(
p
i
,
p
j
)
=
gcd
(
p
i
,
p
j
)
\min(p_i,p_j) = \gcd(p_i,p_j )
min(pi,pj)=gcd(pi,pj),即
∑
l
≤
i
<
j
≤
r
[
p
i
∣
p
j
]
∣
∣
[
p
j
∣
p
l
]
\sum_{l \le i<j\le r}[p_i|p_j]||[p_j|p_l]
l≤i<j≤r∑[pi∣pj]∣∣[pj∣pl]
题解
分析1
(瞎搞)离线做法,将询问看成 pair sort 一下。维护一个树状数组,
从后往前枚举左边界L,每左移一格时在树状数组对应于L后面的 p [ L ] p[L] p[L]的倍数或约数的位置加一。
这个树状数组a[r]的含义是(L,r)区间中是r的倍数或约数的个数。
每次遇到一个询问区间,求一下 s u m ( L , R ) sum(L,R) sum(L,R)就是答案。
p
i
p_i
pi的约数有
τ
(
p
i
)
\tau(p_i)
τ(pi) 个,倍数有
n
/
p
i
n/p_i
n/pi个,我们枚举了所有数的所有约数和倍数,倍数总和显然是调和级数,而约数个数为:
∑
i
=
1
n
τ
(
i
)
=
∑
i
=
1
n
∑
j
=
1
n
[
j
∣
i
]
=
∑
i
=
1
n
⌊
n
i
⌋
\sum_{i=1}^{n}\tau(i)=\sum_{i=1}^{n}{\sum_{j=1}^{n}{[j|i]}}=\sum_{i=1}^{n}\lfloor\frac {n}{i}\rfloor
i=1∑nτ(i)=i=1∑nj=1∑n[j∣i]=i=1∑n⌊in⌋
所以插入的时间复杂度上界为(降序排列)
T
(
n
)
=
n
n
+
∑
(
n
i
)
⋅
l
o
g
n
=
n
n
+
n
l
o
g
n
l
o
g
n
T(n)=n\sqrt n+\sum(\frac{n}{i})\cdot logn=n\sqrt n+n lognlogn
T(n)=nn+∑(in)⋅logn=nn+nlognlogn
n n n\sqrt n nn的部分只是找约数常数很小,所以200ms就过了,和正解一样。
分析2
对于一个排列 , 所有满足题面描述的二元组
(
i
,
j
)
(i,j)
(i,j)的数量是
n
log
n
n \log n
nlogn级别,可全找出来,然
后对询问离线, 即是一个二维偏序查询问题,用树状数组统计即可。
具体来说,它给了 n log n n \log n nlogn个闭区间 [ i , j ] [i,j] [i,j],询问m个区间 [ L , R ] [L,R] [L,R],有几个 [ i , j ] [i,j] [i,j]区间在 [ L , R ] [L,R] [L,R]之中。
从大到小枚举第一维l,将区间r对应的树状数组a[r]++,每次碰到询问L,就求 s u m ( l , r ) sum(l,r) sum(l,r),代表当前左区间在L左边且有区间<R的区间有几个。
复杂度 n log n log n n \log n\log n nlognlogn,运行时间为cin1402ms, scanf292ms
代码1
#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_back
typedef long long ll;
typedef pair<int,int> pi;
const int MAXN = (int)2e5+7;
template<typename T,size_t size>
struct BIT {
T a[size]; int n;
void init(int _n){n=_n;memset(a,0,sizeof(T)*(_n+1));}
void erase(int x) { for(int i=x;i<=n;i+=i&-i)a[i]=0; }
void update(int x,T y){
//debug(x);
for(int i=x;i<=n;i+=i&-i)a[i]+=y;}
T query(int x){T re=0; for(int i=x;i;i-=i&-i)re+=a[i];return re;}
};
BIT<ll,MAXN>bit;//你数组所开最大的值
int N,M;
int a[MAXN],p[MAXN];
struct Node{
int l,r,id;
Node(int l = 0,int r = 0,int id =0 ):l(l),r(r),id(id){}
bool operator < (const Node&a) const {
return l < a.l;
}
}e[MAXN];
int ans[MAXN];
inline void update(int val,int wei) {
int sq = sqrt(val+0.5);
rep(i,1,sq) {
if (val%i==0) {
int loc = p[i];
int loc2 = p[val/i];
if (loc > wei) {
//debug(1);
bit.update(loc,1);
}
if (loc2 > wei){
//debug(2);
bit.update(loc2,1);
}
}
}
if (sq*sq==val && p[sq] > wei) bit.update(p[sq],-1);
for(int j = 2*val;j <= N;j += val) {
int loc = p[j];
if (loc > wei) bit.update(loc,1);
}
}
int main()
{
scanf("%d %d",&N,&M);
bit.init(N+2);
rep(i,1,N) {
scanf("%d",&a[i]);
p[a[i]] = i;
}
rep(i,1,M) {
int l,r;
scanf("%d %d",&l,&r);
e[i] = Node(l,r,i);
}
sort(e+1,e+1+M);int pl = N;
for(int i = M;i >= 1;i --) {
while (pl >= 1 && pl >= e[i].l) {
update(a[pl],pl);
pl --;
}
int res = bit.query(e[i].r);
ans[e[i].id] = res;
}
rep(i,1,M) {
printf("%d\n",ans[i]);
}
}
/*
4 2
4 3 2 1
1 4
2 4
3
2
*/
代码2
#include<iostream>
#include<map>
#include<string>
#include<cstring>
#include<vector>
#include<algorithm>
#include<set>
#include<sstream>
#include<cstdio>
#include<cmath>
#include<climits>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define per(i,j,k) for(int i = (int)j;i >= (int)k;i --)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_back
#define FAST_IO ios::sync_with_stdio(0); cin.tie(0)
typedef long long ll;
typedef pair<int, int> pi;
const int MAXN = (int)2e5 + 7;
template<typename T, size_t size>
struct BIT {
T a[size]; int n;
void init(int _n) { n = _n; memset(a, 0, sizeof(T)*(_n + 1)); }
void erase(int x) { for (int i = x; i <= n; i += i & -i)a[i] = 0; }
void update(int x, T y) {
//debug(x);
for (int i = x; i <= n; i += i & -i)a[i] += y;
}
T query(int x) { T re = 0; for (int i = x; i; i -= i & -i)re += a[i]; return re; }
};
BIT<ll, MAXN>bit;//你数组所开最大的值
vector<pi> seg;
struct que {
int l; int r; int id;
bool operator<(const que & a)const {
return l < a.l;
}
}Q[MAXN];
int a[MAXN], pos[MAXN];
int ans[MAXN];
vector<int> rs[MAXN];
int main()
{
FAST_IO;
int n,m;
cin >> n >> m;
rep(i, 1, n) {
cin >> a[i];
pos[a[i]] = i;
}
rep(i, 1, m) {
cin >> Q[i].l >> Q[i].r;
Q[i].id = i;
}
rep(i, 1, n) {
for (int j = i * 2; j <= n; j += i) {
int ll = pos[i], rr = pos[j];
if (ll > rr)swap(ll, rr);
rs[ll].push_back(rr);
}
}
sort(seg.begin(), seg.end());
sort(Q + 1, Q + 1 + m);
int sz = seg.size();
int p = m;
bit.init(n);
per(i, n, 1) {
for (auto t : rs[i])bit.update(n+1-t, 1);
while(p>0&&i == Q[p].l) {
ans[Q[p].id] = bit.query(n + 1 - Q[p].l) - bit.query(n + 1 - Q[p].r-1);
p--;
}
}
rep(i, 1, m)cout << ans[i] <<endl;
cin >> n;
}
/*
4 2
4 3 2 1
1 4
2 4
3 2
1 2 3
1 3
2 3
*/