感觉这套题强行撸下来真是超神了,头发都不知道掉了多少。。
一道线段树+最短路的混合使用,强行给CF教育了一发
题目:http://codeforces.com/problemset/problem/787/D
题目大意:
简单点来说就是构造一个图,构造的方法有
1、u → v
2、u → [l,r]
3、[l,r] → u
这三种情况,每种情况都会可以构成权值为c的边
然后问随意一个点到所有点的最短路是多少由于点数为1e5个,总的构图次数为1e5次
有可能构成的边为1e10,首先点数为1e5,构成之后所用dijkstra时间复杂度是O( n2 )远远不够
于是想到了djikstra+heap优化的O( nlogn )的算法,但是复杂度还是不够,乘上点数总时间复杂度为O( n2logn )
所以。。还需要继续优化,怎么优化呢
看到区间,应该能yy到线段树这种鬼畜的数据结构吧。。没错就是线段树
我们通过线段树来减少边的条数。。至于怎么减少呢。。我们可以构建两种线段树,一种是从上指到下的,一种是从下指到上的,他们互相访问的权值为0,
当遇到u → [l,r]这种情况的时候,让他去指向线段树中相应的区间,即可减少边数,我们把线段树的每一个区间都看成一个点,这样就能构成一个最短路。。。
然后同样的[l,r] → u 这种情况,用线段树的区间去指向点,同样把线段树的区间看成点。
线段树所组成的线段,基本上可以看成是O(2)级别的,平均算作是O(2)吧。。
那基本上,你每次无论是点连区间还是区间连点,所构成的线大概也就2条上下所以,建图的时候,点数应该*5倍,但是由于线少了,被log了一下,所以时间复杂度应该改为O( nlog2n )
这个时间复杂度,对于这题就够了。。简直鬼畜。。
敢问世间何时对我们这种弱渣连DIV2都要虐成狗,强行教育了一波。
好像对于pair在G++ 5.0版本不能这么使用,代码只能在G++11.0下编译。
dalao们都说。敲个线段树再敲个dijkstra就过了。。。确实。但是脑洞很大Orz
/*
@resouces: codeforces 787D
@date: 2017-3-27
@author: QuanQqqqq
@algorithm: 线段树 + dijkstra nlogn
*/
#include <bits/stdc++.h>
#define MAXN 500005
#define ll long long
using namespace std;
const ll LLINF = 0x3f3f3f3f3fLL;
typedef pair<ll,int> pli;
struct Edge{
int to,weight;
Edge(int _to = 0,int _weight = 0) : to(_to),weight(_weight){}
};
priority_queue<pli > Q;
vector<vector<Edge> > G(MAXN);
bool vis[MAXN];
int id[MAXN][2];
vector<int> vs;
ll d[MAXN];
int idx;
void addEdge(int from,int to,int weight){
G[from].push_back(Edge(to,weight));
}
void build(int l,int r,int rt,int wh){ //wh0为从上到下的线段树,wh1为从下到上的线段树
id[rt][wh] = ++idx;
if(l == r){
if(wh == 0){
addEdge(id[rt][wh],l,0);
} else {
addEdge(l,id[rt][wh],0);
}
return;
}
int mid = l + r >> 1;
build(l,mid,rt << 1,wh);
build(mid + 1,r,rt << 1 | 1,wh);
if(wh == 0){
addEdge(id[rt][wh],id[rt << 1][wh],0);
addEdge(id[rt][wh],id[rt << 1 | 1][wh],0);
} else {
addEdge(id[rt << 1][wh],id[rt][wh],0);
addEdge(id[rt << 1 | 1][wh],id[rt][wh],0);
}
}
void get(int l,int r,int L,int R,int rt,int wh){
if(L <= l && r <= R){
vs.push_back(id[rt][wh]);
return;
}
int m = l + r >> 1;
if(m >= L){
get(l,m,L,R,rt << 1,wh);
}
if(m < R){
get(m + 1,r,L,R,rt << 1 | 1,wh);
}
}
void dijkstra(int s,int n){
for(int i = 1;i <= n;i++){
vis[i] = false;
d[i] = LLINF;
}
Q.push({-0,s});
d[s] = 0;
while(!Q.empty()){
int u = Q.top().second;
Q.pop();
if(vis[u]){
continue;
}
vis[u] = true;
for(int i = 0;i < G[u].size();i++){
Edge e = G[u][i];
int v = e.to,w = e.weight;
if(w + d[u] < d[v]){
d[v] = w + d[u];
Q.push({-d[v],v});
}
}
}
}
void init(int n){
while(!Q.empty()){
Q.pop();
}
for(int i = 1;i <= n;i++){
G[i].clear();
}
}
int main(){
int n,q,s,l,r,t,u,v,c;
scanf("%d %d %d",&n,&q,&s);
init(n * 5);
idx = n;
build(1,n,1,0);
build(1,n,1,1);
while(q--){
scanf("%d %d",&t,&u);
if(t == 1){
scanf("%d %d",&v,&c);
addEdge(u,v,c);
} else if(t == 2) {
vs.clear();
scanf("%d %d %d",&l,&r,&c);
get(1,n,l,r,1,0);
for(int i = 0;i < vs.size();i++){
addEdge(u,vs[i],c);
}
} else {
vs.clear();
scanf("%d %d %d",&l,&r,&c);
get(1,n,l,r,1,1);
for(int i = 0;i < vs.size();i++){
addEdge(vs[i],u,c);
}
}
}
dijkstra(s,5 * n);
for(int i = 1;i <= n;i++){
if(d[i] == LLINF){
d[i] = -1;
}
printf("%lld ",d[i]);
}
}
另:这套DIV还有dalao直播讲题解。。Orz弱校渣终于得到了dalao的直播真传,厉害了。。
http://www.bilibili.com/video/av9365298/