[JZOJ5133][SDOI省队集训2017]重建

题目描述

这里写图片描述

傻逼题

预处理每个图里经过k条边的最短路。
然后可以用单调栈求出一些关键的c值使得某副图改变最短路经过的边数。
接着顺序扫这些关键点,解方程即可。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef long double db;
const int maxn=1000+10,maxm=10000+10;
const db eps=1e-12;
const ll inf=100000000000000;
struct dong{
    ll c;
    int ca,x;
} a[maxn*2];
ll f[maxn][maxn],g[maxn][maxn],gg;
int h[maxn],go[maxm*2],dis[maxm*2],next[maxm*2],sta[maxn];
ll xy[maxn],ans,p;
db q;
int h2[maxn],g2[maxm*2],d2[maxm*2],n2[maxm*2],b[maxn];
bool bz[maxn];
int i,j,k,l,r,s,t,n,m,tot,top,cnt,num,ca,sa,sb;
void add(int x,int y,int z){
    go[++tot]=y;
    dis[tot]=z;
    next[tot]=h[x];
    h[x]=tot;
}
void add2(int x,int y,int z){
    g2[++top]=y;
    d2[top]=z;
    n2[top]=h2[x];
    h2[x]=top;
}
bool cmp(dong a,dong b){
    return a.c<b.c;
}
ll cil(db q){
    db p=floor(q);
    return ll(p+1);
}
ll getxy(int x,int y){
    if (f[s][x]>f[s][y]) return 0;
    q=(db)(f[s][y]-f[s][x])/(x-y);
    return cil(q);
}
ll getyx(int x,int y){
    if (g[s][x]>g[s][y]) return 0;
    q=(db)(g[s][y]-g[s][x])/(x-y);
    return cil(q);
}
int main(){
    freopen("rebuild.in","r",stdin);freopen("rebuild.out","w",stdout);
    scanf("%d",&ca);
    while (ca--){
        scanf("%d%d%d%d",&n,&m,&s,&t);
        tot=top=0;
        fo(i,1,n) h[i]=h2[i]=bz[i]=0;
        fo(i,1,m){
            scanf("%d%d%d",&j,&k,&l);
            add(j,k,l);add(k,j,l);
        }
        scanf("%d",&num);
        fo(i,1,num){
            scanf("%d",&j);
            b[i]=j;
            bz[j]=1;
        }
        fo(i,1,m){
            j=go[i*2-1];k=go[i*2];l=dis[i*2];
            if (bz[j]&&bz[k]){
                add2(j,k,l);
                add2(k,j,l);
            }
        }
        fo(i,1,n)
            fo(j,0,n)
                f[i][j]=inf;
        f[t][0]=0;
        fo(j,1,n){
            fo(i,1,n){
                r=h[i];
                while (r){
                    f[i][j]=min(f[i][j],f[go[r]][j-1]+dis[r]);
                    r=next[r];
                }
            }
        }
        fo(i,1,num)
            fo(j,0,num)
                g[b[i]][j]=inf;
        g[t][0]=0;
        fo(j,1,num){
            fo(i,1,num){
                r=h2[b[i]];
                while (r){
                    g[b[i]][j]=min(g[b[i]][j],g[g2[r]][j-1]+d2[r]);
                    r=n2[r];
                }
            }
        }
        cnt=0;
        top=0;
        fd(i,n,0){
            if (f[s][i]==inf) continue;
            while (top&&getxy(sta[top],i)<=xy[top]) top--;
            sta[++top]=i;
            if (top>1) xy[top]=getxy(sta[top-1],i);
        }
        sa=sta[1];
        fo(i,1,top-1){
            a[++cnt].c=xy[i+1];
            a[cnt].ca=1;
            a[cnt].x=sta[i+1];
        }
        top=0;
        fd(i,num,0){
            if (g[s][i]==inf) continue;
            while (top&&getyx(sta[top],i)<=xy[top]) top--;
            sta[++top]=i;
            if (top>1) xy[top]=getyx(sta[top-1],i);
        }
        if (!top){
            printf("Impossible\n");
            continue;
        }
        sb=sta[1];
        fo(i,1,top-1){
            a[++cnt].c=xy[i+1];
            a[cnt].ca=2;
            a[cnt].x=sta[i+1];
        }
        sort(a+1,a+cnt+1,cmp);
        ans=-1;
        fo(i,0,cnt){
            if ((i<cnt&&a[i].c!=a[i+1].c)||i==cnt){
                if (sa==sb){
                    if (f[s][sa]==g[s][sb]) ans=(i==cnt?-2:a[i+1].c-1);
                }
                else{
                    gg=f[s][sa]-g[s][sb];
                    r=sb-sa;
                    if (gg%r==0){
                        p=(ll)gg/r;
                        if (p<a[i+1].c&&a[i].c<=p) ans=p;
                    }
                }
            }
            if (i<cnt){
                if (a[i+1].ca==1) sa=a[i+1].x;
                else sb=a[i+1].x;
            }
        }
        if (ans==-1) printf("Impossible\n");
        else if (ans==-2) printf("Infinity\n");
        else printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值