Educational Codeforces Round 69 E Culture Code

E Culture Code

题意:

给定n个布偶,每一个布偶有内部空间 i n [ i ] in[i] in[i],外部体积 o u t [ i ] out[i] out[i],如果 i n [ i ] > = o u t [ j ] in[i] >= out[j] in[i]>=out[j],我们就可以将j套入到i里面,定义一个最打集合嵌套是使得没有再有新的布偶可以加入到这个集合中嵌套进去。我们定义一个集合的cost是浪费的空间,问使得cost最小的集合的数量是多少?

分析:

  1. 首先考虑怎么求最小代价,我们可以将嵌套想象成一个有向无环图,在有向无环图上跑,肯定是dp比较合适,关键是我们不能直接建图,因为数据范围太大了,我们发现具有单调性,就是如果 i n [ i ] > = i n [ j ] in[i] >= in[j] in[i]>=in[j]那么可以嵌套进 j j j的一定可以嵌套进去 i i i,所以我们考虑前缀的思想,一步步地推。
  2. 对所有的布偶按 i n [ i ] in[i] in[i]排序,对所有的布偶按 o u t [ i ] out[i] out[i]排序,按 o u t [ i ] out[i] out[i]排序是为了便于统计布偶的贡献,因为 o u t [ i ] &lt; = i n [ j ] out[i] &lt;= in[j] out[i]<=in[j],排序之后,就可以嵌套,那么如果已经排序 对于所有的 1 &lt; = k &lt; i 1 &lt;= k &lt; i 1<=k<i都可以嵌套
  3. 每一次的最小值怎么递推呢,考虑上一个 i n p r e &lt; i n n o w in_{pre} &lt; in_{now} inpre<innow, 由于 关于 i n p r e in_{pre} inpre的最小值已经统计出来,那么我把相同的木偶嵌套金 i n n o w in_{now} innow, m i n c o s t mincost mincost增加了 i n n o w − i n p r e in_{now}-in_{pre} innowinpre,之后再考虑加入之前不能加入的娃娃,依次更新当前的最小值,并且将计数的值更新
  4. 最后统计所有不能再嵌套的木偶的最小值以及贡献即可

参考代码

const int maxn = 4e5+10;

int out[maxn],in[maxn];
int q[maxn],p[maxn];
LL  dp[maxn],num[maxn];
// dp[i]  代表in第i小的最小空间,num[i] 代表计数
/*
  dp[i] 满足前一个对后一个的贡献可以加入,如果出现更小的,可以更新
*/
int main(void)
{
    int n;
    cin>>n;
    int maxin = 0;
    for(int i= 1;i <= n;++i){
      scanf("%d%d",&out[i],&in[i]);
      q[i] = p[i] = i;
      maxin = max(maxin,in[i]);
    }
    // cout<<endl;
    // DEBUG;
    sort(q+1,q+n+1,[](const int &a,const int &b){return out[a] < out[b];});
    sort(p+1,p+n+1,[](const int &a,const int &b){return in[a] < in[b];});
    int j = 1;

    int  delta = 0;
    int  sum = 0;// 前缀和计算贡献
    for(int i = 1;i <= n; ++i){
      delta = in[p[i]]-in[p[i-1]]+delta;
      while(j <= n && out[q[j]] <= in[p[i]]){
        int d = in[p[i]]-out[q[j]]+dp[q[j]];
        if(d == delta)
          (sum+=num[q[j]])%=mod;
        else if(d < delta)
          delta = d,sum = num[q[j]];
        ++j;
      }

      dp[p[i]] =  delta;
      num[p[i]] = max(sum,1);
      // cout<<i<<" "<<"in "<<dp[p[i]]<<endl;
    }
    // cout<<delta<<endl;
    sum = 0;
    for(int i = 1;i <= n; ++i){
      if(out[i] > maxin)
       {
         if(dp[i] < delta)
          delta = dp[i],sum = num[i];
        else if(dp[i] == delta)
         (sum+=num[i])%=mod;
       }
    }
    // cout<<delta<<endl;
    cout<<sum<<endl;
    

   return 0;
}


```cpp

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值