zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    多表查询
    合并查询与连接查询区别
    union 合并查询语法
    外连接查询left join on 左连接 right join on 右连接
    inner join on 三表查询四表查询5表查询不管多少表都可以
    INEER JOIN..ON两表查询例子
    sql server 三表查询
    两表查询语句
    内连接查询
    转:Exchange Server 2013 一步步安装图解
  • 原文地址:https://www.cnblogs.com/pealfrog/p/15143677.html
Copyright © 2011-2022 走看看