CF-787D-线段树建图+最短路

http://codeforces.com/problemset/problem/787/D

题目大意是给出一个有向图,有N个节点,初始节点在S,询问S到所有点最短路。边的读入方式有三种, 1 u v w  表示 u->v有一条边权为w的边, 2 v l r w ,表示v->[l,r]内的任意一个点支付w即可,

3 v l r w 表示从[l,r]内任意一个点到v支付w即可。直接构图的话可能会出现完全图,被卡死。

  一种巧妙的构图方式是,由这些个区间联想到线段树(然而我并没有想到),我们不妨对2,3两种类型建立两颗线段树 他们的叶子节点是共用的(1--N),对于2来说,如果节点v到树上的某个节点x有一条w的边,

就表示v到这个节点所对应的区间的点都可以支付w到达,并且在2的内部所有的父亲都向自己的儿子建立一条边权为0的边,这样如果v能到达x,说明v能到达x所有的子孙节点(支付w),对于3来说只不过反过来了一下思路一样。

  建完图之后跑最短路就好了,节点数大约N*10够了。

  

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define LL long long
  4 #define ULL unsigned long long
  5 #define pii pair<int,int>
  6 #define mid ((L+R)>>1)
  7 #define lc (id<<1)
  8 #define rc (id<<1|1)
  9 #define pb push_back
 10 #define mp make_pair
 11 #define inf 0x3f3f3f3f
 12 #define linf 0xffffffffffff
 13 const int maxn=100010;
 14 int N,Q,S,T0,T1,CNT;
 15 int ch[maxn*10][2];
 16 LL d[maxn*10];
 17 bool in[maxn*10];
 18 int tot,first[maxn*10];
 19 struct Edge{int v,w,next;}e[maxn*20];
 20 void add(int u,int v,int w){
 21     e[tot].v=v;
 22     e[tot].w=w;
 23     e[tot].next=first[u];
 24     first[u]=tot++;
 25 }
 26 void build1(int &p,int L,int R){
 27     if(L==R) p=L;
 28     else{
 29         p=++CNT;
 30         build1(ch[p][0],L,mid),build1(ch[p][1],mid+1,R);
 31         add(p,ch[p][0],0),add(p,ch[p][1],0);
 32     }
 33 }
 34
 35 void build2(int &p,int L,int R){
 36     if(L==R) p=L;
 37     else{
 38         p=++CNT;
 39         build2(ch[p][0],L,mid),build2(ch[p][1],mid+1,R);
 40         add(ch[p][0],p,0),add(ch[p][1],p,0);
 41     }
 42 }
 43 void insert1(int id,int L,int R,int v,int l,int r,int w){
 44     if(L>=l&&R<=r){
 45         add(v,id,w);
 46         return;
 47     }
 48     if(l<=mid)insert1(ch[id][0],L,mid,v,l,r,w);
 49     if(r>mid)insert1(ch[id][1],mid+1,R,v,l,r,w);
 50 }
 51
 52 void insert2(int id,int L,int R,int v,int l,int r,int w){
 53     if(L>=l&&R<=r){
 54         add(id,v,w);
 55         return;
 56     }
 57     if(l<=mid)insert2(ch[id][0],L,mid,v,l,r,w);
 58     if(r>mid)insert2(ch[id][1],mid+1,R,v,l,r,w);
 59 }
 60 void spfa(){
 61     for(int i=0;i<=CNT;++i)d[i]=linf;
 62     memset(in,0,sizeof(in));
 63     queue<int>q;
 64     q.push(S);
 65     in[S]=1;
 66     d[S]=0;
 67     while(!q.empty()){
 68         int u=q.front();
 69         q.pop();
 70         for(int i=first[u];~i;i=e[i].next){
 71             if(d[e[i].v]>d[u]+e[i].w){
 72                 d[e[i].v]=d[u]+e[i].w;
 73                 if(!in[e[i].v]){
 74                     q.push(e[i].v);
 75                 }
 76             }
 77         }
 78     }
 79     for(int i=1;i<=N;++i) printf("%lld%c",d[i]==linf?-1:d[i],i==N?‘\n‘:‘ ‘);
 80 }
 81 int main()
 82 {
 83     memset(first,-1,sizeof(first));
 84     tot=0;
 85     scanf("%d%d%d",&N,&Q,&S);
 86     CNT=N;
 87     build1(T0,1,N);
 88     build2(T1,1,N);
 89     int opt,u,v,w,l,r;
 90     while(Q--){
 91         scanf("%d",&opt);
 92         if(opt==1){
 93             scanf("%d%d%d",&u,&v,&w);
 94             add(u,v,w);
 95         }
 96         else{
 97             scanf("%d%d%d%d",&v,&l,&r,&w);
 98             if(opt==2){
 99                 insert1(T0,1,N,v,l,r,w);
100             }
101             else{
102                 insert2(T1,1,N,v,l,r,w);
103             }
104         }
105     }
106     spfa();
107     return 0;
108 }
109 /*0 -1-112
110 0 -1 -1 12
111 */

原文地址:https://www.cnblogs.com/zzqc/p/9900367.html

时间: 2024-10-12 02:58:54

CF-787D-线段树建图+最短路的相关文章

POJ 1436 Horizontally Visible Segments(线段树建图+枚举)

题目连接:http://poj.org/problem?id=1436 题意:给一些线段,每个线段有三个值y1, y2, x代表起点为(x, y1),终点为(x, y2)的线段.当从一个线段可以作水平线到另一个线段并且不穿过其他线段时,就称这两个线段时水平可见的.当三个线段可以两两水平可见,就称为形成一个线段三角.问:在这些线段中有多少个这样的线段三角? 分析:可以把每条线段看做是一个点,如果它和其他线段是水平可见的,就将这两个点相连,由于是无向图,就是你能看到我,我也能看到你,所以需要连接两次

HDU5669 Road 分层最短路+线段树建图

分析:(官方题解) 首先考虑暴力,显然可以直接每次O(n^2) ?的连边,最后跑一次分层图最短路就行了. 然后我们考虑优化一下这个连边的过程 ,因为都是区间上的操作,所以能够很明显的想到利用线段树来维护整个图, 连边时候找到对应区间,把线段树的节点之间连边.这样可以大大缩减边的规模,然后再跑分层图最短路就可以了. 但是这样建图,每一次加边都要在O(logn)个线段树节点上加边,虽然跑的非常快,但是复杂度仍然是不科学的. 为了解决边的规模的问题,开两棵线段树,连边时候可以新建一个中间节点,在对应区

线段树建图

对于一个图,n个节点,有向边,求点s到其他所有点的最短路. 题目给的边的方式: u -> v [l,r] -> v v -> [l,r] 这样的话边数是O(n^2)级别的,怎么做? 假设把[1,n]建成segment tree 后,有tot个节点. 则建一个新图,新图有2 * tot + n个节点 新图多了2 * tot个节点,表示2棵线段树A,B 点的编号分类: A - [1,tot] B - [tot + 1,2 * tot] C - [2 * tot + 1,2 * tot + n

[线段树建图 bfs] BZOJ 3073 [Pa2011]Journeys

CA爷的题解:http://blog.csdn.net/CreationAugust/article/details/50739132 #include<cstdio> #include<cstdlib> #include<algorithm> #include<set> using namespace std; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) {

Codeforces 787D. Legacy 线段树优化建图+最短路

output standard output Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them. There are n planets in their universe numbered from 1 to n.

CF786B Legacy(线段树优化建图+最短路)

在qbxt某营集体做的 题解里以及外地OIer基本上都写两颗线段树的 而我们六安的OIer神TM思维一致--只用一颗线段树,类似于一维分层图的思想,第二层上与第一层相对应的结点的编号是第一层结点编号+NUM,而且貌似比分颗的思维正常一点,因为满足lson=k<<1,rson=k<<1|1,和一般的线段树相似度高. 至于为什么要分颗或分层,容易想明白树边(辅助边)必须是双向的(因为要用祖先结点的出入信息),但如果不分颗或分层的话求出来最短路不很明显是0了吗QwQ 所以分层的话父向子应

Hdu 5521 Meeting(建图+最短路)

题目地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=5521 思路:边数太多,不能直接建图.对于每个集合,设置一个虚拟点,对于每个集合中的点u:连一条u->S权值为0的边(点在集合中,花费为0):连一条S->u权值为w的边(从集合中一点到另一点花费w).分别计算从点1到i和从点n到i的最短路,枚举i,则ans=min( ans,max ( dist[0][i],dist[1][i] ) ). #include<queue> #i

uva10067 Playing with Wheels 【建图+最短路】

题目:uva10067 Playing with Wheels 题意:给出一个机器,有四个循环的轮子,见图,然后给出一个初始数和目标数,然后期间不能出现的数字,每一分钟可以拨动一个数,问你最短需要的时间. 分析:这个题目可以转化为求图的最短路. 因为有对于一个当前状态,有8种可以转化为的状态,那么我们可以把每一种状态转化为一个点,然后状态之间连长度 1 的边,然后求一次初始状态到目标状态的最短路. 开始的时候我们每一组数据建图一次,下来0.9s,然后优化了一下,就是在每次建图不能到达的边删除之后

CF 338E, 线段树

这道题真是太伤节奏了,做完之后好几天没动弹了. 题目大意:给你两个数组a.b,a长b短,对于以a每个位置开头与b等长的连续子序列,我们询问是否有这样一个匹配方式让子序列每个数和b中每个数一一对应相加且和大于给定的常数h. 解:没有想明白,去看官解没弄懂出题人是怎么分块瞎搞的.搜了一下题解,发现有一个更巧妙的解法,首先我们对b排序不会影响到结果,然后对于某个数,他在排序后的b里的合法匹配方案一定是连续的一段,这样就转化成了线段问题.对于Bn(从小到大排序的B数组),我们可知必须子序列里有n个数和他