题意翻译
很久很久以前,约翰只会做一种食品;而现在约翰能给他的NNN (1≤N≤40000)(1 \leq N \leq 40000)(1≤N≤40000)头奶牛供应MMM (1≤M≤N)(1 \leq M \leq N)(1≤M≤N)种不同的食品。但奶牛们非常挑剔,iii号奶牛只吃食品PiP_iPi (1≤Pi≤M)(1 \leq P_i \leq M) (1≤Pi≤M)
每天,奶牛们按一定编号排队进自助餐厅用餐。不幸的是,这么多各类的食品会让清扫工作变得非常不方便。如果约翰在一次用餐中供应了KKK种食品,他之后就需要K2K^2K2的时间进行清扫。
为了减少清扫的时间,约翰决定把排好队的奶牛划分成若干组。每一组包含一段连续的奶牛。每一次,只让一组奶牛进入餐厅。这样,他可以让清扫所需的总用时变得最小。请计算这个最小用时。
样例解释:
如此划分:1\2\1\3\22\34343\1\4```
首先分成多少个组不重要。
我们考虑n最大为40000,如果把n分成n个组,那么最小用时为n.所以一个组不能超过√n,如果超过√n个,那么最小用时就大于n...,
所以一个组不能超过√n。所以我们可以枚举以某个点为最后一组的终点。考虑dp啊。
dp[i] = min(dp[i],dp[pos[j] - 1] + j * j); pos[j]表示:第i个位置之前第j个不同的数出现的最早位置。
转移有了,怎么求pos[j]?
nex[i]:记录下一个a[i]出现的位值。
pre[i]:记录上一个a[i]出现的位值。
cnt[j]:从pos[j]到i([pos[j],i])这段区间有多少个不同的数。
last[a[i]]:记录上一个a[i]出现的位置。//用这个来算出前面两个。
对于第i个数,如果上一个a[i]出现的位值<pos[j],那么i 一定是在[pos[j],i]这段区间第一次出现,cnt[j]++;
如果cnt[j] > j,说明pos[j]要往后移,移到cnt[j] == j (其实只进了一个,所以cnt[j]最大比j大1,所以只要找到第一个nex[k]>i,的位置k就好。)
#include<cstdio>
#include<cmath>
int n,m,pos[205],f[40005],p[40005],last[40005],pre[40005],nex[40005],cnt[205];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&p[i]);
pre[i]=last[p[i]];
nex[last[p[i]]]=i;
last[p[i]]=i;
f[i]=1e9;nex[i]=n+1;
}
int t=sqrt(n);for(int i=1;i<=t;i++)pos[i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=t;j++)
{
if(pre[i]<pos[j])cnt[j]++;
if(cnt[j]>j)
{
cnt[j]--;
while(nex[pos[j]]<i)pos[j]++;
pos[j]++;
}
f[i]=min(f[pos[j]-1]+j*j,f[i]);
}
printf("%d\n",f[n]);
}//别人家的代码。
然而我自己也瞎搞了一番,优先队列复杂度拜拜了,然而洛谷上却能A。。。。。
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
int n,m,ha,shu;
int a[40005], nex[40005], pre[40005], vis[40005];
long long dp[40005];
priority_queue<int>q;
priority_queue<int>q1;
struct node
{
int wei;
bool operator <(const node a)const
{
return wei > a.wei;
}
};
priority_queue<node>qq;
void read(int &x)
{
x = 0; int f = 0; char c = getchar();
while(c < '0' || c > '9')
{
if(c == '-') f = 1; c = getchar();
}
while(c >= '0' && c <= '9')
{
x = x * 10 + c - '0'; c = getchar();
}
if(f) x = -x;
}
int main()
{
// freopen("cleanup.10.in","r",stdin);
//freopen("cleanup.out","w",stdout);
read(n);read(m);
int ha = sqrt(n);
for(int i = 1; i <= n; i++)
{
read(a[i]);
if(pre[a[i]]) nex[pre[a[i]]] = i;
pre[a[i]] = i;
}
for(int i = 1; i <= n; i++)
{
dp[i] = 1e18;
if(!nex[i]) nex[i] = n + 1;
if(vis[a[i]] == 0 && shu < ha)
{
shu++;
q.push(i); vis[a[i]] = 1;
}
else if(vis[a[i]] == 0)
{
qq.push((node){i}); vis[a[i]] = 1;
}
}
if(shu == 1)
{
cout << 1;
return 0;
}
if(qq.size() == 0) qq.push((node){n+1});
dp[n+1] = 1e18;dp[1] = 0;
int ri = 0;
for(int i = 1; i <= n; i++)
{
int ee = min(shu,ha);
ri = max(ri,ee);
shu = 0;
node haha = qq.top(); int ma;
ma = haha.wei;//q.pop();
for(int j = ee; j >= 1; j--)
{
if(i % 2)
{
int he = q.top();
q.pop();
dp[ma] = min(dp[i] + j *j, dp[ma]);
ma = he;
shu++;
if(he != i) q1.push(he);
else
{
node hihi = qq.top();
int hi = hihi.wei;
if(nex[i] < hi)q1.push(nex[i]);else {
q1.push(hi);qq.pop();qq.push((node){nex[i]});
}
}
}
else
{
int he = q1.top();
q1.pop();
dp[ma] = min(dp[i] + j *j, dp[ma]);
shu++;
ma = he;
if(he != i) q.push(he);
else
{
node hihi = qq.top();
int hi = hihi.wei;
if(nex[i] < hi)q.push(nex[i]);else {
q.push(hi);qq.pop();qq.push((node){nex[i]});
}
}
}
}
}
cout << dp[n+1];
return 0;
}