Educational Codeforces Round 69 (Rated for Div. 2) E. Culture Code dp(线段树转移) or 最短路计数

题目链接

题意: 

有n个俄罗斯套娃  每个套娃有内积in 外积out  空间浪费指的是 多个(或者一个)套娃组合在一起  最大的套娃内部的空气的体积

问有多少种套娃的组合方式(不能再被其他套娃套住  显然套住别人不会是最小答案 ) 使得空间浪费最小

题解: 

线段树优化dp

  • 设置dp[i].minn表示第i个套娃作为最内部套娃的所有套娃组合 的最小空间浪费 所以显然该状态从 $inj>outi$ 中转移过来
  • 先将n个套娃按照内积排序一遍 方便找到嵌套对象也就是j 
  • 首先显然要倒着dp  二分查找最小的j满足 $inj>out$ 所以 j-n 的套娃均满足转移条件  直接搜索最小值 然后在第i个位置插入 $dp[j].minn-(out[i]-in[i]) ,dp[j].num  $ 即可
  • 该树状数组用结构体方便很多  而且可以优化push_up 为merge (经过队友提醒 tql !!! )
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N],num,minn;
#define  ll long long
ll ans;
const ll mod=1e9+7;
struct node{ll minn,num;}t[N<<2];

node merge(node a,node b)
{
    if(a.minn==b.minn)return (node){a.minn,(a.num+b.num)%mod };
    if(a.minn<b.minn) return (node){a.minn,a.num};
    if(a.minn>b.minn) return (node){b.minn,b.num};
}
void upnode(int x,int v,int num,int l,int r,int pos)
{
    if(l==r){t[pos]=(node){v,num};return;}
    int m=(l+r)>>1;
    if(x<=m)upnode(x,v,num,l,m,pos<<1);
    else upnode(x,v,num,m+1,r,pos<<1|1);
    t[pos]=merge(t[pos<<1],t[pos<<1|1]);
}
node qmin(int L,int R,int l,int r,int pos)
{
    if(L<=l&&r<=R)return t[pos];
    int m=l+r>>1;node ans=(node){1000000000,0};
    if(L<=m)ans=merge(ans,qmin(L,R,l,m,pos<<1));
    if(R>m)ans=merge(ans,qmin(L,R,m+1,r,pos<<1|1));
    return ans;
}
struct NODE{int in,out;}s[N];
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d%d",&s[i].out,&s[i].in);
    sort(s+1,s+1+n,[](NODE a,NODE b){return a.in<b.in;});

    for(int i=n;i>=1;i--)
    {
        int L=1,R=n,ans=-1;
        while(L<=R)
        {
            int mid=(L+R)>>1;
            if(s[mid].in>=s[i].out)ans=mid,R=mid-1;
            else L=mid+1;
        }
        if(ans==-1)upnode(i,s[i].in,1,1,n,1);
        else 
        {
            node u=qmin(ans,n,1,n,1);
            upnode(i,u.minn-(s[i].out-s[i].in),u.num,1,n,1);
        }
    }
    cout<<t[1].num;
    return 0;
}
code

最短路计数

  • 我们要维护的东西是 $in[pk]  + (in[pk-1]-out[pk-1]) + ( in[pk-2] - out[pk-1] ) + ....+(in[pk1]-out[pk1])$ 的最小值及其个数  且满足 $out[pk] > in[n]$
  • 我们可以连图 从$0-n-1  add(i,i+1,in[i+1]-in[i])$  根据这条路径跑的话维护的就是 in[pk]  
  • 有 (in[pk-1] - out[pk-1] ) 可以来优化最短路    设置temp为最小的可以转移的对象 $add(i,temp,in[temp]-out[i])$ 可以发现恰好跑出来的就是上面那条式子
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int mod=1e9+7;
struct node{int in,out;}s[N];
int n,ans,dis[N],cnt[N],vis[N],pos,head[N],minn=1e9;
struct Edge{int to,nex,v;}edge[N<<1];
void add(int a,int b,int c){edge[++pos]=(Edge){b,head[a],c};head[a]=pos;};

int main()
{   
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d%d",&s[i].out,&s[i].in);
    sort(s+1,s+1+n,[](node a,node b){return a.in<b.in;});
    for(int i=0;i<n;i++)add(i,i+1,s[i+1].in-s[i].in);
    for(int i=1;i<=n;i++)
    {
        int temp=-1,L=1,R=n;
        while(L<=R)
        {
            int mid=L+R>>1;
            if(s[mid].in>=s[i].out)temp=mid,R=mid-1;else L=mid+1;
        }
        if(temp==-1)continue;
        else add(i,temp,s[temp].in-s[i].out);
    }
    memset(dis,0x3f,sizeof dis);
    dis[0]=0;cnt[0]=1;
    for(int i=0;i<=n;i++)
    {
        for(int j=head[i];j;j=edge[j].nex)
        {
            int v=edge[j].to;
            if(dis[i]+edge[j].v<dis[v])dis[v]=dis[i]+edge[j].v,cnt[v]=cnt[i];
            else if(dis[i]+edge[j].v==dis[v])cnt[v]+=cnt[i],cnt[v]%=mod;
        }
    }
    for(int i=1;i<=n;i++{if(s[i].out>s[n].in&&dis[i]<minn)minn=dis[i];}
    for(int i=1;i<=n;i++)
    if(s[i].out>s[n].in&&dis[i]==minn)ans=(ans+cnt[i])%mod;
    cout<<ans;
    return 0;
}
Code

猜你喜欢

转载自www.cnblogs.com/bxd123/p/11695048.html