[JSOI2008]Blue Mary开公司

Description

Input
第一行 :一个整数 N ,表示方案和询问的总数。
接下来N行,每行开头一个单词“Query”或“Project”。
若单词为Query,则后接一个整数 T ,表示Blue Mary询问第T天的最大收益。
若单词为Project,则后接两个实数S P ,表示该种设计方案第一天的收益S,以及以后每天比上一天多出的收益 P
1<=N<=100000 1<=T<=50000 0<P<100 |S|<=106
提示:本题读写数据量可能相当巨大,请选手注意选择高效的文件读写方式。

Output
对于每一个Query,输出一个整数,表示询问的答案,并精确到整百元(以百元为单位,
例如:该天最大收益为 210 290 时,均应该输出 2 )。没有方案时回答询问要输出0

Sample Input
10
Project 5.10200 0.65000
Project 2.76200 1.43000
Query 4
Query 2
Project 3.80200 1.17000
Query 2
Query 3
Query 1
Project 4.58200 0.91000
Project 5.36200 0.39000

Sample Output
0
0
0
0
0

HINT

Source

思路
线段树标记永久化。见我的博客[Heoi2013]Segment

代码

#include <cstdio>

const int maxn=100000;
const int maxx=50000;

struct line
{
  double k,b;

  double f(int x)
  {
    return k*x+b;
  }
};

struct segment_tree
{
  int val[(maxx<<2)+10],totl;
  double topv;
  line li[maxn+10];

  int insert(int now,int left,int right,int insv)
  {
    if(left>right)
      {
        return 0;
      }
    int uol=li[insv].f(left)>li[val[now]].f(left);
    int uor=li[insv].f(right)>li[val[now]].f(right);
    int mid=(left+right)>>1;
    if((!val[now])||(uol&&uor))
      {
        val[now]=insv;
      }
    else if(uol^uor)
      {
        double c=(li[insv].b-li[val[now]].b)/(li[val[now]].k-li[insv].k);
        if((c<=mid)&&uol)
          {
            insert(now<<1,left,mid,insv);
          }
        else if((c<=mid)&&uor)
          {
            insert(now<<1,left,mid,val[now]);
            val[now]=insv;
          }
        else if((c>mid)&&uol)
          {
            insert(now<<1|1,mid+1,right,val[now]);
            val[now]=insv;
          }
        else if((c>mid)&&uor)
          {
            insert(now<<1|1,mid+1,right,insv);
          }
      }
    return 0;
  }

  inline int ins(double a,double b)
  {
    ++totl;
    li[totl].k=b;
    li[totl].b=a-b;
    insert(1,1,maxx,totl);
    return 0;
  }

  int query(int now,int left,int right,int pos)
  {
    double ff=li[val[now]].f(pos);
    if(ff>topv)
      {
        topv=ff;
      }
    if(left==right)
      {
        return 0;
      }
    int mid=(left+right)>>1;
    if(pos<=mid)
      {
        query(now<<1,left,mid,pos);
      }
    if(pos>mid)
      {
        query(now<<1|1,mid+1,right,pos);
      }
    return 0;
  }

  inline int ask(int p)
  {
    topv=0;
    query(1,1,maxx,p);
    int ans=topv/100;
    return ans;
  }
};

segment_tree st;
int n;
char s[10];

int main()
{
  scanf("%d",&n);
  while(n--)
    {
      scanf("%s",s);
      if(s[0]=='P')
        {
          double a,b;
          scanf("%lf%lf",&a,&b);
          st.ins(a,b);
        }
      else
        {
          int a;
          scanf("%d",&a);
          printf("%d\n",st.ask(a));
        }
    }
  return 0;
}
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值