• [BZOJ1584][Usaco2009 Mar]Cleaning Up 打扫卫生


    Description

    有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000。现在Farmer John要把这些奶牛分成若干段,定义每段的不河蟹度为:若这段里有k个不同的数,那不河蟹度为k*k。那总的不河蟹度就是所有段的不河蟹度的总和。

    Input

    第一行:两个整数N,M

    第2..N+1行:N个整数代表每个奶牛的编号

    Output

    一个整数,代表最小不河蟹度

    Sample Input

    13 4
    1
    2
    1
    3
    2
    2
    3
    4
    3
    4
    3
    1
    4

    Sample Output

    11


    这题真的...打死我都想不到可以用这种方法优化...

    首先设$f[i]$表示$i$之前的最优答案, 那么$f[i] = min(f[j]+dif^2)$。

    这是$O(N^2)$的显然会T。

    我们发现答案的最差值一定是$n$(每一个都当做一组),所以我们用来转移的区间只有不同颜色数量在$[1, sqrt{N}]$之间。

    然后就可以$O(N sqrt{N})$转移了。

    不太会写抄的网上的题解。

    根号算法无处不在。


    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <bitset>
    #include <algorithm>
    using namespace std;
    #define reg register 
    #define gc getchar
    inline int read() {
        int res=0;char ch=gc();bool fu=0;
        while(!isdigit(ch)){if(ch=='-')fu=1;ch=gc();}
        while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=gc();
        return fu?-res:res;
    }
    #define N 40005
    int n, m;
    int a[N], pre[N], nxt[N], pos[N], lst[N], cnt[N];
    int f[N];
    
    int main() 
    {
        n = read(), m = read();
        for (reg int i = 1 ; i <= n ; i ++) a[i] = read(), pre[i] = -1, nxt[i] = n + 1;
        for (reg int i = 1 ; i <= n ; i ++) {
            if (lst[a[i]]) pre[i] = lst[a[i]];
            lst[a[i]] = i;
        }
        memset(lst, 0, sizeof lst);
        for (reg int i = n ; i >= 1 ; i --) {
            if (lst[a[i]]) nxt[i] = lst[a[i]];
            lst[a[i]] = i;
        }
        memset(f, 0x3f, sizeof f);
        f[0] = 0;
        for (reg int i = 1 ; i <= n ; i ++) 
        {
            for (reg int j = 1 ; j * j <= n ; j ++) 
                if (pre[i] <= pos[j]) cnt[j]++;
            for (reg int j = 1 ; j * j <= n ; j ++)
                if (cnt[j] > j) {
                    pos[j] ++;
                    while(nxt[pos[j]] <= i) pos[j]++;
                    cnt[j]--;
                }
            for (reg int j = 1 ; j * j <= n ; j ++) f[i] = min(f[i], f[pos[j]] + j * j);
        }
        cout << f[n] << endl;
        return 0;
    }
  • 相关阅读:
    将文件写进数据库的方法
    立个Flag
    JQuery_学习1
    js制作一个简单的选项卡
    输出数据库中的表格的内容(pdo连接)
    不饮鸡汤的寂寞先生
    详细谈Session
    详细谈Cookie
    php字符串操作函数练习2
    ios开发网络学习五:MiMEType ,多线程下载文件思路,文件的压缩和解压缩
  • 原文地址:https://www.cnblogs.com/BriMon/p/9789116.html
Copyright © 2020-2023  润新知