题意:求a数组的LIS,但是加了一个条件,为了LIS最大 b[i] a[i]可以交换。最多交换m次;
思路:我们令dp[i][j][l]表示i在最长上升子序列中,已经损失j点能量,第i个人转换了ai和bi的最长上升子序列的数目,可以得到方程 dp[i][j][0]=max{dp[k][j][0](a[k]<a[i])+1,dp[k][j][1](b[k]<a[i])+1},dp[i][j][1]=max(dp[k][j-1][0](a[k]<b[i])+1,dp[k][j-1][1](b[k]<b[i])+1)。这样是n^2k的,我们换个思路,即从k能转移到哪些i,我们先将体积离散化,再用m颗线段树来维护已损失j点能量的情况下体积为某数的最长上升子序列。这样可以做到nlgnk,不过线段树常数写的很大的话还是会TLE,考虑到求最大值实际上是求1~x的最大值,这样我们可以通过常数非常小的树状数组来解决。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define maxn 20000 5 using namespace std; 6 7 int n,m; 8 int a[1001],b[1001]; 9 int dp[1001][1001][2]; 10 int c[1001][2000]; 11 int f[2001],cnt; 12 13 inline int Find(int xx) 14 { 15 return lower_bound(f,f+cnt,xx)-f+1; 16 } 17 18 inline void insert(int *c,int x,int cc) 19 { 20 for(; x<=cnt; x+=x&-x) 21 { 22 c[x]=max(c[x],cc); 23 } 24 } 25 26 inline int get_max(int *c,int x) 27 { 28 int ans=0; 29 for( ; x; x-=x&-x) 30 { 31 ans=max(ans,c[x]); 32 } 33 return ans; 34 } 35 36 int main() 37 { 38 int T; 39 scanf("%d",&T); 40 while(T--) 41 { 42 43 int n,m; 44 scanf("%d%d",&n,&m); 45 cnt=0; 46 for(int i=1;i<=n;++i) 47 { 48 scanf("%d%d",a+i,b+i); 49 f[cnt++]=a[i]; 50 f[cnt++]=b[i]; 51 } 52 sort(f,f+cnt); 53 cnt=unique(f,f+cnt)-f; 54 for(int i=1;i<=n;++i) 55 { 56 a[i]=Find(a[i]); 57 b[i]=Find(b[i]); 58 } 59 memset(c,0,sizeof(c)); 60 int ans=0; 61 for(int i=1;i<=n;++i) 62 { 63 for(int j=0;j<=min(m,i);++j) 64 { 65 dp[i][j][0]=get_max(c[j],a[i]-1)+1; 66 ans=max(ans,dp[i][j][0]); 67 if(j) 68 { 69 dp[i][j][1]=get_max(c[j-1],b[i]-1)+1; 70 ans=max(ans,dp[i][j][1]); 71 } 72 } 73 for(int j=0;j<=min(m,i);++j) 74 { 75 insert(c[j],a[i],dp[i][j][0]); 76 if(j) insert(c[j],b[i],dp[i][j][1]); 77 } 78 } 79 printf("%d ",ans); 80 } 81 return 0; 82 }