HDU 5125 magic balls(线段树+DP)

magic balls

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 323    Accepted Submission(s): 90

Problem Description

The town of W has N people. Each person takes two magic balls A and B every day. Each ball has the volume ai and bi. People often stand together. The wizard will find the longest increasing subsequence in the ball A. The wizard has M energy. Each point of energy can change the two balls’ volume.(swap(ai,bi)).The wizard wants to know how to make the longest increasing subsequence and the energy is not negative in last. In order to simplify the problem, you only need to output how long the longest increasing subsequence is.

Input

The first line contains a single integer T(1≤T≤20)(the data for N>100 less than 6 cases), indicating the number of test cases.
Each test case begins with two integer N(1≤N≤1000) and M(0≤M≤1000),indicating the number of people and the number of the wizard’s energy. Next N lines contains two integer ai and bi(1≤ai,bi≤109),indicating the balls’ volume.

Output

For each case, output an integer means how long the longest increasing subsequence is.

Sample Input

2

5 3

5 1

4 2

3 1

2 4

3 1

5 4

5 1

4 2

3 1

2 4

3 1

Sample Output

4

4

题意是给两个序列 a , b ..

然后问最多用m次操作( swap(ai,bi) ),使得序列a的最长上升子序列的长度最长

不难想出一个DP就是,dp[i][j][k] 表示最长子序列中最后一个元素是i ,用了j 次操作,k表示元素i有没进行交换(0表示无,1表示有)。

然后转移就是

dp[i][j][0] = max { dp[i][j][0] , dp[k][j][0] }  (  i = 1~ n , j = 0~i , k = 1 ~ i -1 , a[k] < a[i] )

dp[i][j][0] = max { dp[i][j][0] , dp[k][j][1] }  (  i = 1~ n , j = 0~i , k = 1 ~ i -1 , b[k] < a[i] )

dp[i][j][1] = max { dp[i][j][1] , dp[k][j-1][0] }  (  i = 1~ n , j = 0~i-1 , k = 1 ~ i -1 , a[k] < b[i] )

dp[i][j][1] = max { dp[i][j][1] , dp[k][j-1][1] }  (  i = 1~ n , j = 0~i , k = 1 ~ i -1 , b[k] < b[i] )

O(n)枚举状态第一维 , O(n)枚举状态第二维。

再用线段树或者树状数组O(log n)来更新状态就行了。

用m棵线段树,每棵线段树表示用了j次操作( j = 0~m ) 。

每棵线段树的每个叶子结点的位置表示数值的大小,区间l~r维护的是l~r数值范围dp的最大值。

那么先将a,b序列离散后,数值范围是0~2000。

然后当我们要更新 dp[i][j][0] 的时候,就第j棵线段树找出1~a[i]-1的结点中,用dp的最大值+1 去更新。

dp[i][j][1],就第j - 1 棵线段树找出1~b[i]-1的结点中,用dp的最大值+1 去更新。

注意。假设我们已经维护出dp[i][j][k] , 先不要把状态插入线段树,因为有可能影响到dp[i][j+1][k]的更新。

那么,在更新dp[i+1][][] 之前 , 把dp[i][][]的所有状态插进线段树就不会影响到更新了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <algorithm>

using namespace std;

#define root 1,n<<1|1,1
#define lr rt<<1
#define rr rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define X first
#define Y second
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1010 ;
const int M = 2010 ;
const int inf = 1e9+7;

int n , m , a[N] , b[N] ;

//----------------------------
int date[N][M<<2];

void Up( int id , int rt ) {
    date[id][rt] = max( date[id][lr] , date[id][rr] ) ;
}

void Build( int id , int l , int r , int rt ) {
    date[id][rt] = 0 ;
    if( l == r ) return ;
    int mid = (l+r)>>1;
    Build(id,lson),Build(id,rson);
    Up(id,rt);
}

void Update( int id , int l , int r , int rt , int x , int val ) {
    if( l == r ) {
        date[id][rt] = max( date[id][rt] , val ) ;
        return ;
    }
    int mid = (l+r)>>1;
    if( x <= mid ) Update(id,lson,x,val);
    else Update(id,rson,x,val);
    Up(id,rt);
}

int Query( int id , int l , int r , int rt , int L , int R ) {
    if( l == L && r == R ) {
        return date[id][rt];
    }
    int mid = (l+r)>>1;
    if( R <= mid ) return Query(id,lson,L,R);
    else if( L > mid ) return Query(id,rson,L,R);
    else return max( Query(id,lson,L,mid) , Query(id,rson,mid+1,R) );
}
//----------------------------------

struct node { int x , id , xx ; }e[N<<2];
bool cmp1( const node &a , const node &b ) { return a.x < b.x ; }
bool cmp2( const node &a , const node &b ) { return a.id < b.id ; }

void Read() {
    cin >> n >> m ;
    for( int i = 0 ; i < 2 * n ; ++i ){
        cin >> e[i].x ; e[i].id = i ;
    }
    sort( e , e + 2 * n , cmp1 );
    e[0].xx = 2 ;
    for( int i = 1 ; i < 2 * n ; ++i ){
        e[i].xx = ( e[i].x == e[i-1].x ? e[i-1].xx : e[i-1].xx + 1 );
    }
    sort( e , e + 2 * n , cmp2 );
    int tot = 0 ;
    for( int i = 1 ; i <= n ; ++i ) a[i] = e[tot++].xx , b[i] = e[tot++].xx ;

}

vector<pii>A,B;

void Run() {
    int ans = 1 ;
    for( int i = 0 ; i <= m ; ++i ) Build( i , root );
    for( int i = 1 ; i <= n ; ++i ) {
        A.clear() , B.clear();
        for( int j = 0 ; j <= min( i , m ) ; ++j ) {
                int tmpa = Query( j , root , 1 , a[i] - 1 ) + 1 ;
                ans = max( ans , tmpa ) ; A.push_back(pii(j,tmpa));
                if( !j ) continue ;
                int tmpb = Query( j - 1 , root , 1 , b[i] - 1 ) + 1 ;
                ans = max( ans , tmpb ) ; B.push_back(pii(j,tmpb));
            }
        for( int j = 0 ; j < A.size() ; ++j ) Update( A[j].X ,root , a[i] , A[j].Y );
        for( int j = 0 ; j < B.size() ; ++j ) Update( B[j].X ,root , b[i] , B[j].Y );
    }
    cout << ans << endl ;
}

int main()
{
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif // LOCAL
    ios::sync_with_stdio(false);
    int _ ; cin >> _ ;
    while( _-- )  Read() , Run() ;
}

时间: 2024-12-19 04:10:08

HDU 5125 magic balls(线段树+DP)的相关文章

hdu 5125 magic balls(dp)

题目链接:hdu 5125 magic balls #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1005; int N, M, dp[maxn][maxn], A[maxn], B[maxn]; int vec[maxn][maxn], c[maxn]; void init () { scanf("%d%d&quo

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

HDU 3016 Man Down 线段树+简单DP

囧,一开始看错题意,后来才发现人是垂直下落的,被附带链接里的Man Down游戏误导了. 那就变成了一个简单的DAG模型动态规划,随意搞就ok了 #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include <queu

[HDU 6447][YJJ&#39;s Salesman][2018CCPC网络选拔赛 1010][离散化+线段树+DP]

链接: http://acm.hdu.edu.cn/showproblem.php?pid=6447 题意: 左上角(0,0),右下角(10^9,10^9)的网格,其中有n(1<=n<=10^5)个方格内有权值. 一次只能沿右,下,右下三个方向走一个格子,只有沿右下方向走到格子里才可以获得权值. 问从(0,0)到(10^9,10^9)的路径最大权值是多少. 思路: 网格路径权值问题,第一感考虑DP,x从上往下,y从左往右刷表,状态转移方程为dp[i][j]=max(dp[i-1][j],dp[

HDU 3874 Necklace (线段树单点更新+区间查询+离线操作)

Problem Description Mery has a beautiful necklace. The necklace is made up of N magic balls. Each ball has a beautiful value. The balls with the same beautiful value look the same, so if two or more balls have the same beautiful value, we just count

HDU 4902 Nice boat(线段树)

HDU Nice boat 题目链接 题意:给定一个序列,两种操作,把一段变成x,把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 思路:线段树,每个结点多一个cover表示该位置以下区间是否数字全相同,然后每次延迟操作,最后输出的时候单点查询即可 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1

hdu 2795 Billboard(线段树)

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10890    Accepted Submission(s): 4827 Problem Description At the entrance to the university, there is a huge rectangular billboard of

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu

HDU 3954 Level up 线段树

---NotOnlySuccess 出的题--- 看了题之后觉得和HDU 4027有点像,给的K很小,只有10,目测只要有人升级的时候直接更新到叶子节点就ok了.不过不同于HDU 4027 的是,那题每一次更新都相当于这题的一次升级操作,这题里面可能会出现一次操作之后没有升级和出现升级两种情况,一时半会没了思路. 无奈去搜题解,发现我只要维护一个区间当中距离升级最近的人所需要的基础升级经验,即不算等级加成的裸的升级经验,如果在一次涨经验之后,出现当前区间当中有人会升级,直接将每一个要升级的人更新