Codeforces 1497E2 线性筛 + 尺取法 + DP

题意

Codeforces 1497E2 Square-free division (hard version)

题解

根据算数基本定理,设 a = ∑ p i e i a=\sum p_i^{e_i} a=piei。若两个数字的乘积为完全平方数,则两个数字指数项 e i e_i ei 为奇数的质因子 p i p_i pi 构成的集合相同,那么可以用这样的质因子的乘积 ∏ p i \prod p_i pi 唯一的表示。线性筛 O ( N ) O(N) O(N) 预处理出 [ 1 , A ] [1,A] [1,A] 的最小质因数,则可以 O ( N log ⁡ A ) O(N\log A) O(NlogA) 求解每个元素的唯一表示。

则同一段中的限制条件转化为不存在两个数字的唯一表示相同。枚举最后一段的左端点, d p [ i ] [ j ] dp[i][j] dp[i][j] 表示从元素 [ 1 , i ] [1,i] [1,i] 中改变不超过 j j j 个数字可以划分的最小段数,总时间复杂度 O ( N 2 K 2 ) O(N^2K^2) O(N2K2),显然难以胜任。观察到 j j j 相同时, d p [ i ] [ j ] dp[i][j] dp[i][j] 随着 i i i 递增而单调不减,那么若能求解出以 i i i 为右端点改变不超过 j j j 个数字的满足条件区间的最左侧的端点 l b [ i ] [ j ] lb[i][j] lb[i][j],则 D P DP DP 总时间复杂度减少为 O ( N K 2 ) O(NK^2) O(NK2)
d p [ i ] [ j ] = min ⁡ 0 ≤ k ≤ j d p [ l b [ i ] [ k ] − 1 ] [ j − k ] + 1 dp[i][j]=\min\limits_{0\leq k\leq j}dp[lb[i][k]-1][j-k]+1 dp[i][j]=0kjmindp[lb[i][k]1][jk]+1 观察到 j j j 相同时,随着满足条件的区间右界的左移,左界不会向右移动,则可以使用尺取法 O ( K N ) O(KN) O(KN) 求解 l b [ i ] [ j ] lb[i][j] lb[i][j]

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005, maxk = 22, maxa = 10000005, inf = 0x3f3f3f3f;
int T, N, K, A[maxn], id[maxn];
int np, prime[maxa], minp[maxa];
int cnt[maxa], lb[maxn][maxk], dp[maxn][maxk];

void sieve()
{
    
    
    for (int i = 2; i < maxa; ++i)
    {
    
    
        if (!minp[i])
            minp[i] = i, prime[++np] = i;
        for (int j = 1; j <= np && i * prime[j] < maxa; ++j)
        {
    
    
            minp[i * prime[j]] = prime[j];
            if (minp[i] == prime[j])
                break;
        }
    }
}

int main()
{
    
    
    sieve();
    scanf("%d", &T);
    while (T--)
    {
    
    
        scanf("%d%d", &N, &K);
        for (int i = 1; i <= N; ++i)
            scanf("%d", A + i);
        for (int i = 1; i <= N; ++i)
        {
    
    
            int a = A[i], x = 1, cnt = 0, lst = 0;
            while (a != 1)
            {
    
    
                int p = minp[a];
                if (p == lst)
                    ++cnt;
                else
                {
    
    
                    if (cnt & 1)
                        x *= lst;
                    cnt = 1, lst = p;
                }
                a /= p;
            }
            if (cnt & 1)
                x *= lst;
            id[i] = x;
        }
        for (int j = 0; j <= K; ++j)
        {
    
    
            int l = N + 1, sum = 0;
            for (int i = N; i; --i)
            {
    
    
                while (l - 1 >= 1 && sum + (cnt[id[l - 1]] > 0) <= j)
                    --l, sum += cnt[id[l]] > 0, ++cnt[id[l]];
                lb[i][j] = l;
                sum -= --cnt[id[i]] > 0;
            }
        }
        for (int i = 1; i <= N; ++i)
            for (int j = 0; j <= K; ++j)
                dp[i][j] = inf;
        for (int j = 0; j <= K; ++j)
            dp[0][j] = 0;
        for (int i = 1; i <= N; ++i)
            for (int j = 0; j <= K; ++j)
                for (int k = 0; k <= j; ++k)
                    dp[i][j] = min(dp[i][j], dp[lb[i][k] - 1][j - k] + 1);
        printf("%d\n", dp[N][K]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/115266153