Hướng dẫn giải của Bedao Mini Contest 08 - PAIRS


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Tác giả: bedao

Subtask 1

Duyệt toàn bộ cặp ~1 \le i < j \le n~ và kiểm tra 2 điều kiện: ~i \times j \le m~ và ~i \times j~ là số chính phương.

Độ phức tạp: ~\mathcal{O}(n^2)~

Subtask 2

Nhận thấy ~i \le \sqrt{m}~. Với mỗi ~i~, ta duyệt ~j~ từ ~i+1~ đến ~\min(n,m/i)~. Với mỗi cặp ~(i,j)~ ta chỉ cần kiểm tra điều kiện ~i \times j~ có phải số chính phương hay không.

Độ phức tạp: ~\mathcal{O}(m\log m)~

Subtask 3

Với mỗi số ~i~, ta gọi ~p[i] = i / x~ với ~x~ là số chính phương lớn nhất mà ~i~ chia hết. Ta có thể tính được ~p[i]~ bằng cách phân tích thừa số nguyên tố của số ~i~ ra và đặt ~p[i] = ~ tích các ước nguyên tố có lũy thừa lẻ. Khi đó chi phí chuẩn bị mảng ~p~ là ~\mathcal{O}(n\log n)~

Nhận xét: tích hai số ~i~ và ~j~ là số chính phương khi và chỉ khi ~p[i] = p[j]~. Với mỗi ~j~, ta sẽ đếm số lượng ~i < j~ mà ~p[i]=p[j]~ và ~i \times j \le m~. Ta có thể làm việc này bằng hai con trỏ, chi phí là ~\mathcal{O}(n)~.

Độ phức tạp: ~\mathcal{O}(n \log n)~

Subtask 4

Ta sẽ dựng mảng ~p~ nhanh hơn như sau:

for (int i = 1; i <= n; i++) 
    p[i] = i;
for (int i = 2; i*i <= n; i++) {
    for (int j = i*i; j <= n; j += i*i)
        p[j] = j / (i*i);
}

Với cách này chi phí xây dựng mảng ~p~ sẽ là ~\mathcal{O}(n)~.

Độ phức tạp: ~\mathcal{O}(n)~

Code mẫu

#include <iostream>
#include <stdio.h>
#include <vector>
#include <cmath>
#include <math.h>
#include <map>
#include <algorithm>
#include <set>
#include <bitset>
#include <queue>
#include <cstring>
#include <stack>
#include <iomanip>
#include <assert.h>

#define BIT(x,pos) (((x)>>(pos)) & 1LL)
#define _(x) (1LL<<(x))
#define bitCnt(x) __builtin_popcountll(x)
#define turnOn(x,pos) ((x) = (_(pos)))
#define turnOff(x,pos) ((x) &= ~(_(pos)))
#define flipBit(x,pos) ((x) ^= (_(pos)))
#define lowBit(x) ((x) & (-x))
#define turnAll(x) (_(x)-1LL)

#define name "test"
#define nameTask ""
#define fastIO ios::sync_with_stdio(false); cin.tie(0);

using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<ll> vll;

const int N = 1E7;

int f[N+9];
int p[N+9];
int n;
ll m;
ll ans = 0;

int main()
{
    if (fopen(name".INP","r"))
        freopen(name".INP","r",stdin),
        freopen(name".OUT","w",stdout);

    fastIO

    cin>>n>>m;

    for (int i = 1;i <= n; ++i) p[i] = i;

    for (int i = 2;i*i <= n; ++i)
        for (int j = i * i;j <= n; j += i * i)
            p[j] = j / (i*i);

    for (int i = 1;i <= n; ++i) f[p[i]]++;

    for (int R = n, L = 1; L <= R;)
    {
        if (1LL * L * R <= m)
        {
            ans += (f[p[L]] - 1);
            --f[p[L]];
            ++L;
        }
        else
        {
            --f[p[R]];
            --R;
        }
    }

    cout<<ans;
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.