题意:有n个空心物品,每个物品有外部体积outi和内部体积ini,如果ini>outj,那么j就可以套在i里面。现在我们要选出n个物品的一个子集,这个子集内的k个物品全部套在一起,且剩下的物品都无法添加到这个子集中(没有空间塞进去)。
定义浪费的空间为子集中空心的部分,即ini1+(ini2−outi1)+(ini3−outi2)+⋯+(inik−outik−1)ini1+(ini2−outi1)+(ini3−outi2)+⋯+(inik−outik−1)。求浪费空间最少的子集个数。
解法:第一时间能想到最短路计数,但是朴素建图办法是n^2的。不会线段树优化建图,这里学习的是https://www.cnblogs.com/birchtree/p/11274812.html这位大佬的。
上面大佬的博客说得十分好了。线段树优化的原理其实就是通过一棵线段树当作工具树,这棵树不附带信息,只是作为一个桥梁连原图结点,且因为线段树能极短地表示区间的优点使得:向区间连边时极大的优化边数。
从而达到优化边数的目的。
upd:好像被新数据hack了
#include<bits/stdc++.h> using namespace std; const int N=1e6+10; const int MOD=1e9+7; int n,s,t,tot,p[N],b[N],tag[N],indeg[N],outdeg[N]; typedef long long LL; struct dat{ int l,r; bool operator < (const dat &rhs) const { return r<rhs.r; } }a[N]; int cnt=0,head[N],nxt[N<<2],to[N<<2],len[N<<2]; void add_edge(int x,int y,int z) { nxt[++cnt]=head[x]; to[cnt]=y; len[cnt]=z; head[x]=cnt; indeg[y]++; outdeg[x]++; } void build(int rt,int l,int r) { tot=max(tot,rt); if (l==r) { p[l]=rt; return; } int mid=l+r>>1; add_edge(rt,rt<<1,0); add_edge(rt,rt<<1|1,0); build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); } void query(int rt,int l,int r,int ql,int qr,int i) { if (ql<=l && r<=qr) { add_edge(tot+i,rt,a[i].l); return; } int mid=l+r>>1; if (ql<=mid) query(rt<<1,l,mid,ql,qr,i); if (qr>mid) query(rt<<1|1,mid+1,r,ql,qr,i); } queue<int> q; LL dis[N],ans[N]; LL toposort() { memset(dis,0x3f,sizeof(dis)); memset(ans,0,sizeof(ans)); for (int i=1;i<=n;i++) { tag[i]+=tag[i-1]; if (tag[i]==0) add_edge(s,tot+i,0); else add_edge(p[i],tot+i,-a[i].r); } q.push(s); dis[s]=0; ans[s]=1; add_edge(s,1,0x3f3f3f3f); while (!q.empty()) { int x=q.front(); q.pop(); for (int i=head[x];i;i=nxt[i]) { int y=to[i]; if (dis[x]+len[i]<dis[y]) { dis[y]=dis[x]+len[i]; ans[y]=ans[x]; } else if (dis[x]+len[i]==dis[y]) { ans[y]=(ans[x]+ans[y])%MOD; } if (--indeg[y]==0) q.push(y); } } return ans[t]; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d%d",&a[i].r,&a[i].l); sort(a+1,a+n+1); tot=0; build(1,1,n); //for (int i=1;i<=n;i++) add_edge(p[i],tot+i,-a[i].r); for (int i=1;i<=n;i++) b[i]=a[i].r; s=0; t=tot+n+1; for (int i=1;i<=n;i++) { int tmp=upper_bound(b+1,b+i+1,a[i].l)-b-1; if (tmp>=1) query(1,1,n,1,tmp,i),tag[1]++,tag[tmp+1]--; else add_edge(tot+i,t,0); } cout<<toposort()<<endl; return 0; }