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; }
最短路计数
- 我们要维护的东西是 $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; }