• 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
    ",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
    ", ans);
        }
        return 0;
    }
    /*
    1
    7 1
    13 13 1 13 3 1 3
    
     */
    
  • 相关阅读:
    bzoj2298 [HAOI2011]problem a
    P5504 [JSOI2011]柠檬
    洛谷P4383 [八省联考2018]林克卡特树
    [USACO17DEC]Standing Out from the Herd
    bzoj3926: [Zjoi2015]诸神眷顾的幻想乡
    dtoj4680. 红黑兔
    dtoj2099. 字符串查询( find)
    dtoj1721. 字符串生成器 ( strgen )
    dtoj4542. 「TJOI / HEOI2016」字符串
    loj2278. 「HAOI2017」字符串
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/14649568.html
Copyright © 2020-2023  润新知