TOJ 1611 Moo University - Financial Aid -- 线段树 + DP

9 篇文章 0 订阅
5 篇文章 0 订阅

题目链接:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1611

题目大意:给定c个二元组(x, y),要求从中选出n(n为奇数)个,满足这n个二元组的y之和不超过给定的f,而且x的中位数最大。输出最大的中位数。

分析:先将二元组(记为cow)按X排序,然后枚举每个X作为中位数。这时需要在X之前和X之后各选n / 2个数,因为已经按X排序,不管怎么选都可以满足X是中位数的条件,所以只需要各自选取Y最小的n / 2个数,判断它们的和是否满足不超过f这个条件。如果满足,X可行,否则不行。如果按X从大到小枚举X,那么遇到的第一个可行X即为答案。剩下的问题在于如何高效地求出最小的n / 2个数的和。

可以用动态规划解决这个问题。用一个长为n / 2的数组a记录以cow[i].X为中位数时前面最小的n / 2个Y,同时用dp[i]表示这些数中的最大值的下标,那么计算dp[i + 1]时,如果cow[i + 1].Y >= a[dp[i]],则dp[i + 1] = dp[i]。否则用cow[i + 1].Y替换a[dp[i]],同时计算出新的最大值下标也就是dp[i + 1]。这涉及单点更新及全区间的最大值查询,用线段树可以实现。至于求和,在计算dp[i]的时候顺便就可以求出来了。对于cow[i].X之后的数,再进行一次同样的操作。下面的代码中的dp保存的就是n / 2个数的和,因为最大值下标可以直接从线段树中读出,不需要另外存储。求出dp之后再进行一次线性扫描就可以求出答案了。

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #include 
         #include 
         
           #include 
          
            #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                 
                   #include 
                  
                    #define mp make_pair #define X first #define Y second #define MEMSET(a, b) memset(a, b, sizeof(a)) using namespace std; typedef unsigned int ui; typedef long long ll; typedef unsigned long long ull; typedef pair 
                   
                     pii; typedef vector 
                    
                      vi; typedef vi::iterator vi_it; typedef map 
                     
                       mii; typedef priority_queue 
                      
                        pqi; typedef priority_queue 
                       
                         , greater 
                        
                          > rpqi; typedef priority_queue 
                         
                           pqp; typedef priority_queue 
                          
                            , greater 
                           
                             > rpqp; const int MAX_C = 100000 + 2; const int MAX_N = 10000 + 2; pii cow[MAX_C]; int a[MAX_N]; int dp[2][MAX_C]; int cnt; struct { int left; int right; int max_index; inline int mid() { return (left + right) >> 1; } } st[MAX_N * 4]; void build(int l, int r, int idx) { st[idx].left = l; st[idx].right = r; if (l == r) st[idx].max_index = cnt++; else { int mid = st[idx].mid(), lc = idx << 1, rc = lc | 1; build(l, mid, lc); build(mid + 1, r, rc); int lm = st[lc].max_index, rm = st[rc].max_index; st[idx].max_index = a[lm] > a[rm] ? lm : rm; } } void update(int pos, int idx) { if (st[idx].left != st[idx].right) { int mid = st[idx].mid(), lc = idx << 1, rc = lc | 1; if (pos <= mid) update(pos, lc); else update(pos, rc); int lm = st[lc].max_index, rm = st[rc].max_index; st[idx].max_index = a[lm] > a[rm] ? lm : rm; } } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int n, c, f, i; cin >> n >> c >> f; for (i = 0; i < c; ++i) scanf("%d%d", &cow[i].X, &cow[i].Y); //先处理n = 1时的特殊情况 if (n == 1) { int ans = -1; for (int j = 0; j < c; ++j) if (cow[j].Y <= f) ans = max(ans, cow[j].X); cout << ans << endl; return 0; } sort(cow, cow + c); //计算dp[0] for (i = 0; i < (n >> 1); ++i) dp[0][n >> 1] += (a[i] = cow[i].Y); cnt = 0; build(0, (n >> 1) - 1, 1); for (; i < c - (n >> 1); ++i) { int mi = st[1].max_index; if (a[mi] > cow[i].Y) { dp[0][i + 1] = dp[0][i] - a[mi] + cow[i].Y; a[mi] = cow[i].Y; update(mi, 1); } else dp[0][i + 1] = dp[0][i]; } //计算dp[1] for (i = 0; i < (n >> 1); ++i) dp[1][c - (n >> 1) - 1] += (a[i] = cow[c - 1 - i].Y); cnt = 0; build(0, (n >> 1) - 1, 1); for (i = c - (n >> 1) - 1; i > (n >> 1); --i) { int mi = st[1].max_index; if (a[mi] > cow[i].Y) { dp[1][i - 1] = dp[1][i] - a[mi] + cow[i].Y; a[mi] = cow[i].Y; update(mi, 1); } else dp[1][i - 1] = dp[1][i]; } for (i = c - (n >> 1) - 1; i >= (n >> 1); --i) if (dp[0][i] + dp[1][i] + cow[i].Y <= f) break; if (i >= (n >> 1)) cout << cow[i].X << endl; else cout << -1 << endl; return 0; } 
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
       
      
      
     
     
    
    
   
   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值