%你赛第2期题解

T1自由之翼 的 题解

这道题是Kirito_Rivaille神仙出的

分析

这么智障的题,就不讲了。
再做不出来,你们真是SLZ。

CODE:

自己YY一下不就有了,不发了。

T2虫群之心 的 题解

这道题是wyxdrqc大佬出的

但是wyxdrqc比较懒,不想写题解,那就让他讲吧。
不发了。

T3虚空之遗 的 题解

很明显的最短路啊。这是窝这个蒟蒻出的qwq。

分析:

看完题后,我们发现这道题的建图很奇怪,不是常规的从一点到另一点的连边,有的边是从区间到区间的,这个时候点操作就不是很好办。那好像不太好建图,没法跑图上算法。

但其实是可以的,有一种叫 线段树优化建图 的东西可以实现图上区间连边操作。

线段树优化建图:

我知道你想说这和线段树有什么关系,但其实你这样想,我们有2棵线段树:入与出。(出表示从这里出发,入表示进到了这个点)。
于是每条区间加边就可以转为log级别。

具体可以么干:

建两棵线段树。
第一棵线段树的儿子向父亲连边,第二棵线段树的父亲向儿子连边。
第二棵线段的儿子节点向第一棵线段树的儿子节点连边,
每次连边的时候新建两个节点。
从第一棵线段树对应的区间向第一个点连边。
从第一个点向第二个点连边。
从第二个点向第二棵线段树对应的区间连边。
把第一棵线段树对应的节点当做每个点的起点。

真正的Solution:

窝前面说了这么多,你们难道就不考虑下线段树优化建图吗?
所以,我们考虑下怎么用线段树优化建图来求解。
建立两颗线段树,A线段树每个节点和其父亲节点之间有一条边,边权为0
B线段树每个节点和其两个儿子节点之间有一条边,边权为0
B线段树的每个叶节点和A线段树的每个叶节点之间有一条边,边权为0
对于每一个 $ op1 $ 的操作,我们建一条从A线段树叶节点到B线段树叶节点的边,边权为w
对于每一个 $ op
2 $ 的操作,我们建一条从A线段树叶节点到B线段树区间节点的边,边权为w 因为B线段树是从上向下建,那么就相当于u节点可以到B区间节点的区间内的每一个节点。
对于每一个 $ op==3 $ 的操作,我们建一条从A线段树区间节点到B线段树叶子节点的边,边权为w 因为A线段树我们是从下向上建,那么就相当于A区间节点内的每一个点都可以到达B点的叶子节点。

最后跑一个最短路就可以了

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
 
using namespace std;

#define lson (i<<1)
#define rson (i<<1|1)
#define ll long long

typedef pair< ll, int > pii;

const ll inf =1e18+5;
const int N =1e5+5;
 
struct node {
    int l,r;
    int num;
}ta[N<<2],tb[N<<2];
 
int pa[N],pb[N];
struct node1 {
    ll w;
    int v;
    node1(ll _w,int _v):w(_w),v(_v){}
};

vector< node1 >ve[N*8];
int vis[N*8],tot;
ll dis[N*8],w;
int opt,l,r,u,v;
int n,m,q,s;
 
void builda(int i,int l,int r) {// 自下向上建边
    ta[i].l = l; 
    ta[i].r=r; 
    ta[i].num = ++tot;
    if(ta[i].l == ta[i].r) {
        pa[l] = ta[i].num;
        return ;
    }
    int mid = (ta[i].l + ta[i].r) >> 1;
    builda(lson , l , mid);
    builda(rson , mid + 1 , r);
    ve[ta[lson].num].push_back(node1(0,ta[i].num));
    ve[ta[rson].num].push_back(node1(0,ta[i].num));
}
 
void updatea(int i,int l,int r,int num,ll w) {
    if(ta[i].l == l && ta[i].r == r) { // cong l,r dao u
        ve[ta[i].num].push_back(node1(w,num)); // 从a线段树的区间向b线段树的底部建一条边
        return ;
    }
    int mid = (ta[i].l + ta[i].r) >> 1;
    if(r <= mid) updatea(lson,l,r,num,w);
    else if(l > mid) updatea(rson,l,r,num,w);
    else {
        updatea(lson,l,mid,num,w);
        updatea(rson,mid+1,r,num,w);
    }
}
void buildb(int i,int l,int r) { // 自上向下建边
    tb[i].l = l; 
    tb[i].r = r; 
    tb[i].num = ++tot;
    if(tb[i].l == tb[i].r) {
        pb[l] = tb[i].num;
        ve[tb[i].num].push_back(node1(0,pa[l]));  
        //从b线段树的底部向a线段树的底部建一条0遍表示两个点在逻辑上是一个点
        return ;
    }
    int mid = (tb[i].l + tb[i].r) >> 1;
    buildb(lson , l , mid);
    buildb(rson , mid + 1 , r);
    ve[tb[i].num].push_back(node1(0,tb[lson].num));
    ve[tb[i].num].push_back(node1(0,tb[rson].num));
}
 
void updateb(int i,int l,int r,int num,ll w) {
    if(tb[i].l == l && tb[i].r == r) {
        ve[num].push_back(node1(w,tb[i].num)); 
        // 从a线段树的底部向b线段树的 区间建一条边
        return ;
    }
    int mid = (tb[i].l + tb[i].r) >> 1;
    if(r <= mid) updateb(lson,l,r,num,w);
    else if(l > mid) updateb(rson,l,r,num,w);
    else {
        updateb(lson,l,mid,num,w);
        updateb(rson,mid+1,r,num,w);
    }
}
void dij(int st) {
    memset(vis,0,sizeof(vis));
    for(int i = 0 ; i <= 8 * n ; i++) 
        dis[i]=inf;
    dis[st] = 0;
    pii tmp;
    priority_queue< pii,vector<pii> ,greater<pii> >q;
    q.push(pii(0,st));
    int u,v;
    while(!q.empty() ) {
        tmp=q.top(); 
        q.pop();
        u = tmp.second;
        if(tmp.first > dis[u]) continue;
        vis[u] = 1;
        //cout<<"****** u "<<u<<" "<<dis[u]<<endl;
        for(int i = 0 ; i < ve[u].size() ; i++) {
            v = ve[u][i].v;
            if(vis[v]) continue;
            if(dis[v] > dis[u] + ve[u][i].w){
                dis[v] = dis[u] + ve[u][i].w;
                q.push(pii(dis[v],v));
            }
        }
 
    }
    for(int i = 1 ; i <= n ; i++) {
        if(dis[pa[i]] != inf) {
            printf("%lld ",dis[pa[i] ]);
        }
        else printf("-1 ");
    } 
    return ;
}
 
int main() {
    scanf("%d %d %d",&n,&m,&s);
    int rta = 1;
    builda(1,1,n);
    int rtb = 1;
    //cout<<"rtb "<<rtb<<endl;
    buildb(rtb,1,n);
    while(m--) {
        scanf("%d",&opt);
        if(opt == 1) { 
            scanf("%d %d %lld",&u,&v,&w);
            ve[pa[u]].push_back(node1(w,pb[v]));
        }
        else if(opt == 2) { // 从 u 到 l,r
            scanf("%d %d %d %lld",&u,&l,&r,&w);
            updateb(rtb,l,r,pa[u],w);
        }
        else if(opt == 3) {  // 从 l,r到u
            scanf("%d %d %d %lld",&u,&l,&r,&w);
            updatea(rta,l,r,pb[u],w);
        }
    }
    dij(pa[s]);
    return 0;
}
posted @ 2018-10-14 11:17  西窗夜雨  阅读(174)  评论(0编辑  收藏  举报