• 【UR #2】跳蚤公路


    【UR #2】跳蚤公路

    参照yjc方法。也就是地铁环线那个题。

    求每个点不在负环内的x的取值范围。然后所有1到j能到i的j的范围取交。得到答案。

    每个边形如kx+b的直线,每个环也是

    每个点不在负环内的x取值范围是区间

     

    两次二分,

    第一次二分区间左端点,第二次右端点。

    如果没有负环,左端点往左偏,右端点往右偏

    否则,记录负环的构成:k*mid+b的k的正负,可以得到mid应该往哪里偏。

     

    注意SPFA找负环:

    记录has[x]表示到x的最短路已经经过了多少个点,

    dis[x]最短路,fr[x]是最短路的前驱,pre[x]是最短路前驱指向x的边

    发现has[x]>n的时候,证明出现了负环。但是x不一定在负环上

    不断跳fr[x]找到整个环重复的第一个点z。

    再fr[z]找到整个环。

     

    emmm,一个问题是,负环上的点不会被其他点松弛导致fr[*]找不到负环吗?

    由于SPFA的BFS性质,以及has[x]>n才会判断出有负环,

    所以整个负环上的点,在判断has[*]>n之前,要么不会被松弛、或者松弛后要么找到新的负环、要么会被这个负环再次松弛,

    总之这个环确实能找出来。

    代码:

    目前(2019.6.17)UOJ最优解

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define fi first
    #define se second
    #define mk(a,b) make_pair(a,b)
    #define numb (ch^'0')
    #define pb push_back
    #define solid const auto &
    #define enter cout<<endl
    #define pii pair<int,int>
    using namespace std;
    typedef long long ll;
    template<class T>il void rd(T &x){
        char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
    template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
    template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
    template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('
    ');}
    namespace Modulo{
    const int mod=998244353;
    il int ad(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    il int sub(int x,int y){return ad(x,mod-y);}
    il int mul(int x,int y){return (ll)x*y%mod;}
    il void inc(int &x,int y){x=ad(x,y);}
    il void inc2(int &x,int y){x=mul(x,y);}
    il int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
    template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);}
    template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);}
    }
    // using namespace Modulo;
    namespace Miracle{
    const int N=105;
    const int M=10005;
    const ll inf=1e14;
    int n,m;
    struct edge{
        int x,y,k,b;
    }b[M];
    struct node{
        int nxt,to;
        int k,b;
        ll val(ll x){
            return k*x+b;
        }
    }e[2*M];
    int hd[N],cnt;
    void add(int x,int y,int k,int b){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        e[cnt].k=k;e[cnt].b=b;
        hd[x]=cnt;
    }
    int c[N],df,dfn[N],low[N];
    int scc;
    int f[N][N];
    int sta[N],top,in[N];
    
    int sz[N];
    void tarjan(int x){
        dfn[x]=low[x]=++df;
        sta[++top]=x;in[x]=1;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }else if(in[y]) low[x]=min(low[x],dfn[y]);
        }
        if(low[x]==dfn[x]){
            ++scc;
            int z;
            do{
                z=sta[top--];
                in[z]=0;
                c[z]=scc;
                ++sz[scc];
            }while(z!=x);
        }
    }
    struct seg{
        ll l,r;
        seg(){l=-inf,r=inf;}
        seg(ll le,ll ri){
            l=le;r=ri;
        }
        bool empty(){
            return l>r;
        }
        bool full(){
            return (l==-inf)&&(r==inf);
        }
        seg friend operator &(seg a,seg b){
            return seg(max(a.l,b.l),min(a.r,b.r));
        }
        seg friend operator |(seg a,seg b){
            if(a.empty()) return b;
            if(b.empty()) return a;
            return seg(min(a.l,b.l),max(a.r,b.r));
        }
    }lim[N];
    ll dis[N];
    int pre[N];
    int fr[N];
    int has[N];
    queue<int>q;
    bool vis[N];
    int spfa(int s,ll mid,int n){
        //-1 need small; 1: need big ;0 ok
        while(!q.empty()) q.pop();
        memset(dis,0x3f,sizeof dis);
        memset(vis,0,sizeof vis);
        // memset(pre,0,sizeof pre);
        // memset(has,0,sizeof has);
    
        dis[s]=0;has[s]=1;
        q.push(s);
        while(!q.empty()){
            int x=q.front();q.pop();
            // cout<<" xx "<<x<<endl;
            vis[x]=0;
            
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                if(dis[y]>dis[x]+e[i].val(mid)){
                    dis[y]=dis[x]+e[i].val(mid);
                    pre[y]=i;
                    fr[y]=x;
                    has[y]=has[x]+1;
    
                    if(has[y]>n){//has fuhuan 
                        // cout<<" fuhuan !!!"<<endl;
                        int z=y;
                        int k=0;
                        memset(vis,0,sizeof vis);
                        do{
                            // cout<<" zz "<<z<<endl;
                            // k+=e[pre[z]].k;
                            vis[z]=1;
                            z=fr[z];
                        }while(!vis[z]);
    
                        int lp=z;
                        do{
                            k+=e[pre[z]].k;
                            z=fr[z];
                        }while(z!=lp);
    
                        if(k<0){
                            return -1;
                        }else{
                            return 1;
                        }
                    }
    
                    if(!vis[y]){
                        vis[y]=1;
                        q.push(y);
                    }
                }
            }
        }
        return 0;
    }
    void calc(int s,int id){
        // cout<<" calc "<<id<<" s "<<s<<endl;
        ll al=inf+1;
        ll L=-inf,R=inf;
        while(L<=R){
            ll mid=(L+R)>>1;
            // cout<<L<<" "<<R<<" : "<<mid<<endl;
            int lp=spfa(s,mid,sz[id]);
            if(lp==-1){
                R=mid-1;
            }else if(lp==1){
                L=mid+1;
            }else{
                al=mid;
                R=mid-1;
            }
        }
        // cout<<" al "<<al<<endl;
        ll ar=-inf-1;
        L=-inf,R=inf;
        while(L<=R){
            ll mid=(L+R)>>1;
            
            int lp=spfa(s,mid,sz[id]);
            if(lp==-1){
                R=mid-1;
            }else if(lp==1){
                L=mid+1;
            }else{
                ar=mid;
                L=mid+1;
            }
        }
        // cout<<" ar "<<ar<<endl;
        lim[id]=seg(al,ar);
    }
    int main(){
        rd(n);rd(m);
        for(reg i=1;i<=m;++i){
            rd(b[i].x);rd(b[i].y);rd(b[i].b);rd(b[i].k);
            add(b[i].x,b[i].y,233,233);
            // f[b[i].x][b[i].y]=1;
        }
        for(reg i=1;i<=n;++i){
            if(!dfn[i]){
                tarjan(i);
            }
        }
        // cout<<" after tarjan "<<endl;
        for(reg i=1;i<=scc;++i){
            int s=0;
            memset(hd,0,sizeof hd);
            cnt=0;
            for(reg j=1;j<=m;++j){
                if(c[b[j].x]==i&&c[b[j].y]==i){
                    s=b[j].x;
                    add(b[j].x,b[j].y,b[j].k,b[j].b);
                }
            }
            if(s) calc(s,i);
        }
    
    
        for(reg i=1;i<=scc;++i) f[i][i]=1;
        for(reg i=1;i<=m;++i){
            f[c[b[i].x]][c[b[i].y]]=1;
        }
        for(reg k=1;k<=scc;++k){
            for(reg i=1;i<=scc;++i){
                for(reg j=1;j<=scc;++j){
                    f[i][j]|=(f[i][k])&(f[k][j]);
                }
            }
        }
        for(reg i=1;i<=n;++i){
            seg ans;
            for(reg j=1;j<=scc;++j){
                if(f[c[1]][j]&&f[j][c[i]]){
                   ans=ans&lim[j]; 
                }
            }
            if(ans.empty()){
                puts("0");
            }
            else if(ans.l==-inf||ans.r==inf){
                puts("-1");
            }else{
                printf("%lld
    ",ans.r-ans.l+1);
            }
        }
        return 0;
    }   
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
    */

    普通二分+判负环

    因为整个值域都有单调性。知道不合法往哪里走。

    区间二分?+找负环

    二分左端点,二分右端点。

    麻烦的是,第一次不合法,该往哪里走?(显然之后不合法其实是知道往哪里走的)

    因为并没有单调性。

    本题提供的思路是,考虑不合法的构成,来限制往哪里走才可能合法。

    也就是额外记录一些东西

    (好像这个套路暂时只出现于k*mid+b的k正负判断?)


    upda:2019.6.21

    官方题解:

    利用Bellman-ford的思想进行判断负环。

    简介Bellman-ford的方法:

    不断枚举边数,用所有的边更新点的dis。

    设f[i][j]走了i条边,到达j点,最短路

    如果存在负环,则一定存在一个负环上的点u,使得f[u][n]<f[u][n-1]

    扩展一下

    f[i][j][k]表示,走了i条边,到达j点,斜率是k的最小的b值。

    找到每个点不在负环的x的取值范围。

    min(f[u][n][k1]+k1*x)>=min(f[u][n-1][k2]+k2*x)的x的个数。

    枚举每个k1,求f[u][n][k1]+k1*x>=min(f[u][n-1][k2]+k2*x)的x区间,再求并。

    转化为:求f[u][n][k1]+k1*x<min(f[u][n-1][k2]+k2*x)的x区间,求并,再求补集。

    再枚举k2,解出的所有不等式f[u][n][k1]+k1*x<f[u][n-1][k2]+k2*x求交。

    由于最后答案补集一定是[-inf,l],[l,r],[r,inf],空集,的一种。所以过程中求交求并不会有奇怪情况。

    求并的时候sort一下就好了。

    然后floyd,从1到u到x的u的区间求交。

    虽然不是所有的负环上的点都会考虑到,但是每个负环上至少有一个点可以得到限制,使得没有负环。

    最后是求交,所以不影响正确性。

  • 相关阅读:
    2.2.3 线程中断
    java枚举 用于声明持久化常量 和volley 请求头
    java获取昨天的日期
    使用LocalBroadcastManager
    大端模式与小端模式
    Android更新主线程UI的两种方式handler与runOnUiThread()
    android dialog圆角显示及解决出现的黑色棱角.(友情提示)
    html5中的新标签
    html中的title和alt
    Android开发之蓝牙Socket
  • 原文地址:https://www.cnblogs.com/Miracevin/p/11039649.html
Copyright © 2020-2023  润新知