Gym - 102174 G - 神圣的 F2 连接着我们(线段树优化建图 + 多起点最短路)

题目: 传送门

思路:
网上几乎没有这题的题解,GYM上面过的人也很少,写这篇博客的初衷只是为了有想要看代码的同学了解一下.

思路跟题解差不多,对于每个区间我们用虚点表示,类似于线段树的编号方式,然后建立两个线段树,一颗为出边(内部自下而上建边),一颗为进边(内部自上而下建边)。进边线段树的叶子节点向出边线段树的对应叶子节点建一条有向边。

对于我们m次的加边操作,我先求出a,b两个区间的编号有哪些,然后设立两个虚点 c1,c2 ,a和c1全都建一条为0边,c1 c2建一条花费为我们当前这条新边的花费, c2 和b 再全建一条边.

最后的结果就是多起点的最短路问题,基本做法是把所有起点在开始的时候都放进队列,然后进行的基本的最短路即可.

最后要注意的是数组大小的计算.本菜鸡RE了一晚上才发现这道题要建的边远远超过了自己的数组大小…

Ac_Code

#include 
#include 
#include 
#include 
#include 
#define fir first
#define sec second
using namespace std;
 
typedef pair<int,long long> P;
 
const int maxn = 1e5+7;
const long long INF = 1e18;
 
 
int n,m,pi,qi,cnt,e,cur;
int Node[maxn<<4];
int head[maxn<<4];
P edge[maxn*80];
int nex[maxn*80];
int h1,h2;
int vx[maxn];
int vy[maxn];
 
 
void add(int x,int y,long long cost) {
    //printf("%d %d %lld!\n", x,y,cost);
    edge[++e] = P(y,cost);
    nex[e] = head[x];
    head[x] = e;
}
 
void build1(int l,int r,int num) {
    //printf("This %d %d %d\n",l,r,num);
    cnt = max(cnt,num);
    if(l==r) {
        Node[l] = num;
        return ;
    }
    int mid = (l+r) >>1;
    build1(l,mid,num<<1);
    build1(mid+1,r,num<<1|1);
    add(num<<1,num,0);
    add(num<< 1|1,num,0);
}
 
void build2(int l,int r,int num) {
    if(l==r) {
        add(num+cnt,num,0);
        return ;
    }
    int mid = (l+r) >>1;
    build2(l,mid,num<<1);
    build2(mid+1,r,num<<1|1);
    add(num+cnt,(num<<1)+cnt,0);
    add(num+cnt,(num<<1|1)+cnt,0);
}
 
void quriy1(int l,int r,int num,int le,int ri) {
    if(le<=l && r<=ri) {
        vx[h1++] = num;
        return ;
    }
    int mid = (l+r) >> 1;
    if(le<=mid) quriy1(l,mid,num<<1,le,ri);
    if(mid< ri) quriy1(mid+1,r,num<<1|1,le,ri);
}
 
void quriy2(int l,int r,int num,int le,int ri) {
    if(le<=l && r<=ri) {
        vy[h2++] = num;
        return ;
    }
    int mid = (l+r) >> 1;
    if(le<=mid) quriy2(l,mid,num<<1,le,ri);
    if(mid< ri) quriy2(mid+1,r,num<<1|1,le,ri); 
}
 
int sta[maxn],las[maxn];
long long dis[maxn<<4];
int vis[maxn<<4];
int flag[maxn<<4];
struct cmp1
{
    bool operator () (P a,P b) {
        return a.sec>b.sec;
    }
};
 
priority_queue< P,vector<P>,cmp1> q;
 
void disjike() {
    for(int i = 0;i<=cur;i++) dis[i] = INF;
    for(int i=0;i<qi;i++) {
        int u = Node[sta[i]+n];
        dis[u] = 0;
        q.push(P(u,0));
    }
    while(!q.empty()) {
        P top = q.top();
        q.pop();
        if(vis[top.fir] == 1 || dis[top.fir] == INF) continue;
        vis[top.fir] = 1;
        for(int i=head[top.fir];i;i=nex[i]) {
            P x = edge[i];
            if(dis[x.fir] > dis[top.fir] + x.sec) {
                dis[x.fir] = dis[top.fir] + x.sec;
                q.push(P(x.fir,dis[x.fir]));
            }
        }
    }
    return ;
}
 
 
int main() {
    scanf("%d%d%d%d",&n,&m,&pi,&qi);
    build1(1,n+n,1);
    build2(1,n+n,1);
    cur = cnt*2;
    for(int i=0;i<m;i++) {
        int l,r,a,b;
        long long c;
        scanf("%d%d%d%d%lld",&l,&r,&a,&b,&c);
        h1=h2=0;
        if(c == 0) continue;
        quriy1(1,n+n,1,l,r);
        quriy2(1,n+n,1,a+n,b+n);
        for(int i=0;i<h1;i++) add(vx[i],cur+1,0);
        for(int i=0;i<h2;i++) add(cur+2,vy[i]+cnt,0);
        add(cur+1,cur+2,c);
        cur+=2;
        for(int i=0;i<h2;i++) add(vy[i],cur+1,0);
        for(int i=0;i<h1;i++) add(cur+2,vx[i]+cnt,0);
        add(cur+1,cur+2,c);
        cur+=2;
    }
    for(int i = 0;i<pi;i++) {
        scanf("%d",&las[i]);
    }
    for(int i = 0;i<qi;i++) {
        scanf("%d",&sta[i]);
    }
    disjike();
    long long ans = 0;
    for(int i=0;i<pi;i++) {
        ans = max(ans,dis[Node[las[i]]]);
    }
    if(ans == INF) printf("boring game\n");
    else printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(GYM,线段树)