%你赛第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
对于每一个 $ op2 $ 的操作,我们建一条从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;
}