二维偏序 I query @ The Preliminary Contest for ICPC Asia Xuzhou 2019

二维偏序 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 &lt; j ≤ r [ p i ∣ p j ] ∣ ∣ [ p j ∣ p l ] \sum_{l \le i&lt;j\le r}[p_i|p_j]||[p_j|p_l] li<jr[pipj][pjpl]

题解

分析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=1nτ(i)=i=1nj=1n[ji]=i=1nin
所以插入的时间复杂度上界为(降序排列)
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
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值