CF1197E Culture Code 题解

Codeforces
Luogu

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;
}
原文地址:https://www.cnblogs.com/pealfrog/p/15143677.html