Educational Codeforces Round 69 E - Culture Code (最短路计数+线段树优化建图)

时间:2024-01-21 16:31:51

题意:有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+;
const int MOD=1e9+;
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=,head[N],nxt[N<<],to[N<<],len[N<<];
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>>;
add_edge(rt,rt<<,); add_edge(rt,rt<<|,);
build(rt<<,l,mid);
build(rt<<|,mid+,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>>;
if (ql<=mid) query(rt<<,l,mid,ql,qr,i);
if (qr>mid) query(rt<<|,mid+,r,ql,qr,i);
} queue<int> q;
LL dis[N],ans[N];
LL toposort() {
memset(dis,0x3f,sizeof(dis));
memset(ans,,sizeof(ans));
for (int i=;i<=n;i++) {
tag[i]+=tag[i-];
if (tag[i]==) add_edge(s,tot+i,);
else add_edge(p[i],tot+i,-a[i].r);
}
q.push(s); dis[s]=; ans[s]=;
add_edge(s,,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]==) q.push(y);
}
}
return ans[t];
} int main()
{
scanf("%d",&n);
for (int i=;i<=n;i++) scanf("%d%d",&a[i].r,&a[i].l);
sort(a+,a+n+);
tot=;
build(,,n);
//for (int i=1;i<=n;i++) add_edge(p[i],tot+i,-a[i].r);
for (int i=;i<=n;i++) b[i]=a[i].r;
s=; t=tot+n+;
for (int i=;i<=n;i++) {
int tmp=upper_bound(b+,b+i+,a[i].l)-b-;
if (tmp>=) query(,,n,,tmp,i),tag[]++,tag[tmp+]--;
else add_edge(tot+i,t,);
} cout<<toposort()<<endl;
return ;
}