Link.
Description.
有一些二元组 \(\{(a_i,b_i)\}\),\(\forall i\in[1,n],a_i\le b_i\)。
一个选择方案 \(\{k_i\}\) 是合法的,当且仅当 \(\forall i\in[1,l),b_{k_i}\le a_{k_{i+1}}\)。
一个选择方案是最大的,当且仅当在所有合法选择方案中它拥有最多的二元组。
一个选择方案的权值定义为 \(\left(\sum_{i=1}^la_{k_i}\right)-\left(\sum_{i=1}^{k-1}b_{k_i}\right)\)
求所有最大合法方案中能取到最小权值的方案数。
Solution.
高妙图论题。
首先,选择方案最大有个显然的贪心,按照 \(a_i\) 排序。
然后每次选出当前能包括的最小 \(a_i\),取它即可。
不难发现这样一定是最优的。
我们考虑如何构造出一张图,使得边权和权值可以等价,连两种边
- \(i\rightarrow i+1\),权值为 \(a_{i+1}-a_i\) 表示当前这个数不选。
考虑一大堆连着不选权值就是下一个选了的减去上一个选了的,所以正确。 - \(i\rightarrow w\),权值为 \(a_w-b_i\),其中 \(w\) 表示的是下一个能选的。
这样的话,权值就相当于刚好漏了第一个权。 - 设置一个超级源,让它可以到所有点。
鉴于我们刚开始已经有表示不选的边了,我们可以直接向它和第一个点连边
我们对这张图求最短路计数,得到的就应该是答案,因为权值必定是最小的。
但是它为什么一定能取到最多的点呢?
证明:如果一个方案不是最大的,必定存在一个数,它能选却没被选,设为 \(x\)。
我们考虑找到比 \(x\) 小且能包含 \(x\) 的那个元素,设为 \(y\)。
我们尝试证明,如果选择了 \(x\),答案不会变大。
考虑从 \(x\) 到 \(y\) 的路径,我们关注的就两条,如下
- 通过 \(x\rightarrow x+1\) 连过来的。
- 通过 \(x\rightarrow y\) 直接连过来。
考虑第一种的权值,是 \(a_y-a_x\),第二种是 \(a_y-b_x\),显然第二种小。
我们就证明了如果要求最短路,我们的图得到的最短路一定选了最多的元素。
发现图是 DAG,直接拓扑排序即可。
Coding.
点击查看委屈代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),bz=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) bz=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
bz?x=-x:x;
}/*}}}*/
struct node{int l,r;char operator<(node b) const {return l<b.l;}}a[200005];
struct edge{int to,w,nxt;}e[400005];int et,head[200005],n;
int cn[200005];ll d[200005];const int P=1000000007;
inline void adde(int x,int y,int w) {e[++et]=(edge){y,w,head[x]},head[x]=et;}
int main()
{
read(n);for(int i=1;i<=n;i++) read(a[i].r),read(a[i].l);
sort(a+1,a+n+1,[](node a,node b){return a.l<b.l;});
for(int i=0;i<n;i++) adde(i,i+1,a[i+1].l-a[i].l);
for(int i=1;i<=n;i++)
{
int wh=lower_bound(a+1,a+n+1,(node){a[i].r})-a;
if(wh!=n+1) adde(i,wh,a[wh].l-a[i].r);
}
memset(d,0x3f,sizeof(d)),cn[0]=1,d[0]=0;
for(int x=0;x<=n;x++) for(int i=head[x];i;i=e[i].nxt)
{
if(d[e[i].to]>d[x]+e[i].w) d[e[i].to]=d[x]+e[i].w,cn[e[i].to]=cn[x];
else if(d[e[i].to]==d[x]+e[i].w) (cn[e[i].to]+=cn[x])%=P;
}
int rs=0;ll mx=1e18;for(int i=1;i<=n;i++) if(a[i].r>a[n].l) mx=min(mx,d[i]);
for(int i=1;i<=n;i++) if(mx==d[i]&&a[i].r>a[n].l) rs=(rs+cn[i])%P;
return printf("%d\n",rs),0;
}