传送门:Codeforces 834D
题意:给定一个序列,将其分成k段,每段的值定义为其中不同数的个数,问怎么分能使总值最大。
思路:dp[i][j]表示将前j个数分成i段能得到的最大值。显然dp[i][j] = max(dp[i - 1][k - 1] + num[k, j])(1<=k<=j) 其中num[k,j]表示k到j区间中不同数的个数。
暴力枚举的话是k * n^2的,显然不行,考虑优化取max的过程,可以用线段树维护,记录每个数和它前一个数的位置,每次遇到一个数就将该数的前一个位置到该位置更新(+1)。然后每到新的一次循环就重新建树。
代码:
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
#define pi acos(-1)
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define rep(i,x,n) for(int i=x;i<n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
using namespace std;
typedef pair<int,int>P;
const int MAXN=40000;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
vector<int>pos[MAXN];
vector<int>::iterator it;
int a[MAXN], num[MAXN << 2], lazy[MAXN << 2];
int dp[55][MAXN];
void build(int id, int l, int r, int rt)
{
lazy[rt] = num[rt] = 0;
if(l == r)
{
num[rt] = dp[id][l - 1];
return ;
}
int mid = (l + r) >> 1;
build(id, lson);
build(id, rson);
num[rt] = max(num[rt << 1], num[rt << 1 | 1]);
}
void pushdown(int rt)
{
if(lazy[rt] == 0) return ;
num[rt << 1] += lazy[rt];
num[rt << 1 | 1] += lazy[rt];
lazy[rt << 1] += lazy[rt];
lazy[rt << 1 | 1] += lazy[rt];
lazy[rt] = 0;
}
void update(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R)
{
num[rt]++;
lazy[rt]++;
return ;
}
pushdown(rt);
int mid = (l + r) >> 1;
if(L <= mid)
update(L, R, lson);
if(R > mid)
update(L, R, rson);
num[rt] = max(num[rt << 1], num[rt << 1 | 1]);
}
int query(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R)
return num[rt];
pushdown(rt);
num[rt] = max(num[rt << 1], num[rt << 1 | 1]);
int mid = (l + r) >> 1, ans = 0;
if(L <= mid)
ans = max(ans, query(L, R, lson));
if(R > mid)
ans = max(ans, query(L, R, rson));
return ans;
}
int main()
{
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++)
{
scanf("%d", a + i);
pos[a[i]].pb(i);
}
for(int i = 0; i <= k; i++)
dp[i][i] = i;
for(int i = 1; i <= k; i++)
{
build(i - 1, 1, n ,1);
for(int j = 1; j <= n; j++)
{
int pre;
int id = lower_bound(pos[a[j]].begin(), pos[a[j]].end(), j) - pos[a[j]].begin();
if(id == 0)
pre = 1;
else
pre = pos[a[j]][id - 1] + 1;
update(pre, j, 1, n, 1);
dp[i][j] = query(1, j, 1, n, 1);
}
}
//for(int i = 1; i <= k; i++)
//for(int j = 1; j <= n; j++)
//printf("%d%c", dp[i][j], " \n"[j == n]);
cout << dp[k][n] << endl;
return 0;
}