E2. Square-free division (hard version) DP

E2. Square-free division (hard version) DP

题目大意:

给你一个序列 \(a\) ,你需要把这个序列分成几个区间,使得任意一个区间不存在两个数的乘积是一个平方数,你可以进行 \(k\) 次将一个值改成另一个值,问最少可以分成几个区间?

题解:

很容易想到这个是一个 \(DP\) ,然后 \(DP\) 的定义是: \(dp[i][j]\) 表示到 \(i\) 位置,更改了 \(j\) 次的最少的区间。

然后转移也可以很快的想到 \(dp[i][j] = min(dp[i][j],dp[x][k]+1)\)

但是显然这个是一个 \(O(n*n)\) 的转移,然后发现可以预处理一个数组 \(left[i][j]\) 表示从 \(i\) 这个位置,往左修改了 \(j\) 个值的最长的区间长度。

\(left[i][j]\) 首先枚举 \(j\) 然后用双指针扫过来即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e3 + 10;
const int maxm = 2e5 + 10;
int isp[maxn],cnt,v[maxn];
void init() {
    cnt = 0;
    for (int i = 2; i < maxn; i++) {
        if (!v[i]) {
            isp[++cnt] = i;
            v[i] = i;
        }
        for (int j = 1; j <= cnt; j++) {
            if (isp[j] * i >= maxn) break;
            v[isp[j] * i] = isp[j];
            if (i % isp[j] == 0) break;
        }
    }
}
int a[maxm];
map<int,int>mp;
int lc[maxm][22],pre[maxm],vis[maxm],dp[maxm][22];
void deal(int x) {
    int ans = 1, now = a[x];
    for (int i = 1; i <= cnt; i++) {
        int cur = 0;
        while (now % isp[i] == 0) cur++, now /= isp[i];
        if (cur & 1) ans = ans * isp[i];
        if (now == 1) break;
    }
    if (now != 1) ans = ans * now;
    a[x] = ans;
}
/*
 * left[i][j] 表示从i往左修改j的最长的长度
 * pre[i] 表示i这个位置的上一个值在哪
 */
int main() {
    int T;
    init();
    scanf("%d", &T);
    while (T--) {
        int n, k;
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            deal(i);
        }
        mp.clear();
        for (int i = 1; i <= n; i++) {
            pre[i] = mp[a[i]];
            mp[a[i]] = i;
            memset(lc[i], 0, sizeof(lc[i]));
            memset(dp[i], 0x3f, sizeof(dp[i]));
        }
        for (int j = 0; j <= k; j++) {
            for (int i = 1; i <= n; i++) vis[i] = 0;
            int l = 1, r = 1, ans = 0;
            while (r <= n) {
                if (pre[r] >= l) vis[pre[r]]++, ans++;
                while (ans > j) ans-=vis[l],l++;
                lc[r][j] = r - l + 1;
//                printf("lc[%d][%d]=%d l = %d ans = %d\n",r,j,lc[r][j],l,ans);
                r++;
            }
        }
        int ans = 1e9;
        memset(dp[0],0,sizeof(dp[0]));
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= k; j++) {
                for (int x = 0; x + j <= k; x++) {
                    dp[i][j + x] = min(dp[i][j + x], dp[i - lc[i][x]][j] + 1);
                    if (i == n) ans = min(ans, dp[i][j + x]);
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
/*
1
7 1
13 13 1 13 3 1 3

 */
posted @ 2021-04-12 19:11  EchoZQN  阅读(94)  评论(0编辑  收藏  举报