洛谷2943 [USACO09MAR]清理Cleaning Up——轉變枚舉內容的dp
題目:https://www.luogu.org/problemnew/show/P2943
一下想到n^2。然後不會了。
看過TJ之後似乎有了新的認識。
n^2的冗余部分在於當後面那部分的種類數一樣時,只需用最前面的dp轉移過來即可。
所以如果枚舉的是後面那部分的種類數,對於每個種類數記錄一下最前面的dp,也許會好一些。
但是種類也有n種,怎麽辦?
考慮是否需要枚舉全部從1~n。
k*k是一個比較大的數。發現一定有一種情況使得時間花費為n(即全部單個選),所以只需要枚舉k*k<=n的種類數即可。
結果知道了思路卻還是寫不出來。
關鍵在於怎麽在新入一個值之後維護“後面那部分有 j 個種類”。
1)樹狀數組。就用HH的項鏈那樣的思想。
結果發現不能很快找到新的對應地方。
2)(參考TJ)記錄pre,當pre[ i ]<=zy[ j ]的時候zy[ j+1 ]=zy[ j ]。並且倒序什麽的。
發現不對。因為轉移給 j+1 後不一定是最靠前的位置。
3)(再次參考TJ)記錄pre和nxt,當pre[ i ]<=zy[ j ]的時候將zy[ j ]++到合適的第一個位置。
結果還是不行。因為 j 在前面的一些時候其實是不滿 j 個種類的。貿然++會使位置偏大。
那記錄一個cnt表示現在已經出現了幾個種類,按這個枚舉 j 呢?結果還是不行。
因為就算上界改為現在出現了幾個種類,在最開始的時候一些 j 其實還是不滿 j 個種類。
4)(再再次參考TJ)記錄cnt[ j ],當 cnt[ j ] > j 的時候進入while。
當不行的時候就是要多記錄一些東西才行呢……
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int N=4e5+5,M=200; int n,m,a[N],dp[N],pre[N],nxt[N],pos[N],zy[N],cnt[N];int rdn() { int ret=0;char ch=getchar(); while(ch>‘9‘||ch<‘0‘)ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘)(ret*=10)+=ch-‘0‘,ch=getchar(); return ret; } int main() { n=rdn();m=rdn(); memset(dp,1,sizeof dp);dp[0]=0; for(int i=1;i<=n;i++) { a[i]=rdn();pre[i]=pos[a[i]];nxt[pre[i]]=i;pos[a[i]]=i;nxt[i]=n+1; } for(int i=1;i<=n;i++) for(int j=1;j*j<=n;j++) { if(pre[i]<=zy[j])cnt[j]++;//不要cnt[zy[j]] if(cnt[j]>j) { cnt[j]--;while(nxt[zy[j]+1]<=i)zy[j]++;zy[j]++; } dp[i]=min(dp[i],dp[zy[j]]+j*j); } printf("%d",dp[n]); return 0; }
洛谷2943 [USACO09MAR]清理Cleaning Up——轉變枚舉內容的dp