hdu5542 The Battle of Chibi
题意:
给长度为n的数组a,和一个整数m
要求计算数组中长度为m的子序列数量,满足子序列严格递增
答案对1e9+7取模
数据范围:n,m<=1e3
思路:
因为n和m只有1e3,则:
令d[i][j]表示弟以i结尾长度为j的上升子序列数量
容易想到转移方程为:d[i][j]=sigma(d[k][j-1]),其中1<=k<i,且a[k]<a[i]
枚举i,j是O(n^2)的,加上k则为O(n^3),需要优化
考虑到第三维是累加长度为j-1的所有d[k],a[k]<a[i]
可以用二维树状数组c[x][j]存储以值x为结尾的长度为j的上升子序列数量
这样第三维就能降到log(n),总复杂度O(n^2*log(n))
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e3+5;
const int mod=1e9+7;
int d[maxm][maxm];
int c[maxm][maxm];
int xx[maxm];
int a[maxm];
int n,m;
int lowbit(int i){
return i&-i;
}
void add(int x,int len,int t){
while(x<maxm){
c[x][len]+=t;
c[x][len]%=mod;
x+=lowbit(x);
}
}
int ask(int x,int len){
int ans=0;
while(x){
ans+=c[x][len];
ans%=mod;
x-=lowbit(x);
}
return ans;
}
signed main(){
int T;
cin>>T;
int cas=1;
while(T--){
memset(c,0,sizeof c);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
xx[i]=a[i];
}
sort(xx+1,xx+1+n);
int num=unique(xx+1,xx+1+n)-xx-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(xx+1,xx+1+num,a[i])-xx+3;
}
for(int i=1;i<=n;i++){
d[i][1]=1;
add(a[i],1,1);
for(int j=2;j<=m&&j<=i;j++){
d[i][j]=ask(a[i]-1,j-1);
add(a[i],j,d[i][j]);
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans+=d[i][m];
ans%=mod;
}
printf("Case #%d: ",cas++);
cout<<ans<<endl;
}
return 0;
}
CodeForces833 B. The Bakery
题意:
给长度为n的数组a,和一个整数k
要求把数组分成连续的k段,每段的权值是该段中不同数的个数,
输出最大权值和。
数据范围:n<=35000,k<=min(n,50),1<=a(i)<=n
完整题意:
n个蛋糕k个盒子,要求把蛋糕分成来k段,每段连续,分别用盒子装,
每个盒子的价值是盒子内不同蛋糕的数量,要求计算最大价值
思路:
d[i][j]表示前i个盒子装下前j蛋糕的最大价值
col[i][j]表示区间[i,j]中不同数的个数
显然d[1][j]=col[1][j]
考虑在原来的基础上再切一刀,可推出转移方程为:
d[i][j]=max{d[i-1][k]+col[k+1][i]},其中k<j
因为要枚举i,j,k,因此复杂度是O(n^2*k)的
而n最大35000,这个复杂度显然是不满足要求的,想办法优化掉一个n
因为d[i][j]=max{d[i-1][k]+col[k+1][i]},max操作可以想到线段树
但是要先将d[i-1][k]+col[k+1][i]全部计算出来
建立一颗线段树,第k个位置为d[i-1][k-1]
考虑每个数有价值的区间:
每个数有价值的范围由前一个与他相同数的位置决定,例如:
a[3]=5,a[5]=5,则a[5]有价值的区间为[4,5],
通过记录前一个数的位置可以O(n)把每个数有价值的区间求出来
假如一个数j的有价值区间为[a,b],则对线段树的[a,b]区间加1贡献
这样之后线段树中的每个位置就是d[i-1][k]+col[k+1][i]
取[1,j]中的max来更新d[i][j]
---
总结:
dp转移的过程中,一些数据需要直接计算
有时候可以拆成若干小数据分别计算贡献
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=4e4+5;
int laz[maxm<<2],t[maxm<<2];
int d[55][maxm];
int mark[maxm];
int pre[maxm];
int a[maxm];
int n,k;
void pushup(int node){
t[node]=max(t[node*2],t[node*2+1]);
}
void pushdown(int node){
if(laz[node]){
laz[node*2]+=laz[node];
laz[node*2+1]+=laz[node];
t[node*2]+=laz[node];
t[node*2+1]+=laz[node];
laz[node]=0;
}
}
void build(int l,int r,int node,int i){
t[node]=laz[node]=0;
if(l==r){
t[node]=d[i-1][l-1];
return ;
}
int mid=(l+r)/2;
build(l,mid,node*2,i);
build(mid+1,r,node*2+1,i);
pushup(node);
}
void update(int st,int ed,int l,int r,int node){
if(st<=l&&ed>=r){
laz[node]++;
t[node]++;
return ;
}
pushdown(node);
int mid=(l+r)/2;
if(st<=mid)update(st,ed,l,mid,node*2);
if(ed>mid)update(st,ed,mid+1,r,node*2+1);
pushup(node);
}
int ask(int st,int ed,int l,int r,int node){
if(st<=l&&ed>=r){
return t[node];
}
pushdown(node);
int mid=(l+r)/2;
int ans=0;
if(st<=mid)ans=max(ans,ask(st,ed,l,mid,node*2));
if(ed>mid)ans=max(ans,ask(st,ed,mid+1,r,node*2+1));
return ans;
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
pre[i]=mark[a[i]]+1;
mark[a[i]]=i;
}
for(int i=1;i<=k;i++){
build(1,n,1,i);
for(int j=1;j<=n;j++){
update(pre[j],j,1,n,1);
d[i][j]=ask(1,j,1,n,1);
}
}
cout<<d[k][n]<<endl;
return 0;
}