[CF787D]遗产(Legacy)-线段树-优化Dijkstra(内含数据生成器)

Problem 遗产

题目大意

给出一个带权有向图,有三种操作:

1.u->v添加一条权值为w的边

2.区间[l,r]->v添加权值为w的边

3.v->区间[l,r]添加权值为w的边

求st点到每个点的最短路

Solution

首先我们思考到,若是每次对于l,r区间内的每一个点都执行一次加边操作,不仅耗时还耗空间。

那么我们要想到一个办法去优化它。一看到lr区间,我们就会想到线段树对吧。

没错啦这题就是用线段树去优化它。

首先我们建一棵线段树,然后很容易想到,我们只需要把这一棵线段树当做图中的一部分去跑dijkstra,也可以跑出正确答案。

当然这棵树上的每一条边边权都为0。

看到2.3操作,我们需要建两颗线段树,其中一颗线段树是区间到点,另一个是点到区间。

区间到点的线段树是反着连有向边的,这个简单思考一下便可得出。

建树的时候我们需要对于每一个叶子节点连向对应的单点。

这样一来,因为每个区间最多只会有$\log n$个点,我们只需要最多连$\log n$条边就可以完成一次加边操作。

最后跑dijkstra的时候一定要加堆优化,否则会很惨的。。因为边数特别大。。。

当然本题解写的非常之简略,可以去bilibili[嘿嘿嘿]搜索“codeforces div ABC”(好像是这样)就可以搜索到某大神的直播讲题。

好的接下来是扯淡时间,首先我先在自己学校oj上交了一发,然后a掉了。于是我就很高兴地开始写本篇题解。写着写着突然想去cf上面交一发。。然后直接第七个点就爆炸了。对拍以后发现是自己的dijkstra出了问题。

那么现在问题来了,为什么我们的oj上可以ac呢?

调了数据发现我们oj上的数据只有五个点&&前四个点n<5。。。。

于是就继续改,对拍了一个中午都没错。。结果发现自己oj上拿下来的标程是错的。。

然而这个题目我卡了好久好久好久好久啊。。都快调疯了。

最后发现是dijkstra写错了。。。我都想扇死自己了。

最后A掉了以后,搞了一发数据,扔到了oj上,卡掉了80%的Ac代码。

还有一个非常需要注意到的地方是,总的边数经过计算可得知为$6n+q\log n$条边,也就是≥2500000。

点数也要开到5n级别,也就是≥500000。

还有,dijkstra的distance数组一定要开long long不然会炸裂

加边时间复杂度:$O(q\log n)$

最短路时间复杂度$O(n\log(q\log n))$

Data Maker

此题的数据生成器:(建议调数据大小的时候要在全程序范围内修改,否则只修改define部分会跑出来非法的数据,可不能怪我哟嘿嘿嘿)

 1 #include <ctime>
 2 #include <cstdio>
 3 #include <iostream>
 4 #include <cstdlib>
 5 #define maxN 100000
 6 #define maxQ 100000
 7 #define maxW 100000000
 8 #define starT (rand()%100000+1)
 9 using namespace std;
10 int main(){
11     srand((unsigned)time(NULL));
12     freopen("cf787d7.in","w",stdout);
13     int n=maxN,q=maxQ,s=starT;
14     printf("%d %d %d\n",n,q,s);
15     for(int i=1;i<=q;i++){
16         int num=rand()%11+1;
17         if(num==1)printf("1 %d %d %d\n",rand()%maxN+1,rand()%maxN+1,rand()%maxW+900000000);
18         else if(num<7){
19             int l=rand()%10000+1;
20             printf("2 %d %d %d %d\n",rand()%maxN+1,l,l+30000+rand()%(60000)+1,rand()%maxW+900000000);
21         }else{
22             int l=rand()%10000+1;
23             printf("3 %d %d %d %d\n",rand()%maxN+1,l,l+30000+rand()%(60000)+1,rand()%maxW+900000000);
24         }
25     }
26 }

AC Code

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <queue>
  4 #include <cstring>
  5 #include <algorithm>
  6 #include <cstdlib>
  7 using namespace std;
  8 int n,eq,s,u,v,l,r,Tsk,nodetot,now,tot=0;
  9 priority_queue< pair<long long,int> > q;
 10 struct node{
 11     int to,next,w;
 12 }e[7000070];
 13 bool vis[700050];
 14 int h[700050],lch[700040],w;
 15 long long dist[700050];
 16 void add(int u,int v,int w){
 17     e[++tot].to=v;e[tot].next=h[u];h[u]=tot;e[tot].w=w;
 18 }
 19 int read(){
 20     int x=0,f=1;
 21     char c=getchar();
 22     for (;!isdigit(c);c=getchar())if(c==‘-‘)f=-1;
 23     for (;isdigit(c);c=getchar())x=x*10+c-‘0‘;
 24     return x*f;
 25 }
 26 void addTree(int v,int l,int r,long long w,int nl,int nr,int now){
 27     if(nl>=l&&nr<=r)add(v,now,w);
 28     else{
 29         int mid=(nl+nr)>>1;
 30         if(r<=mid)addTree(v,l,r,w,nl,mid,lch[now-n]);
 31         else if(l>mid)addTree(v,l,r,w,mid+1,nr,lch[now-n]+1);
 32         else{
 33             addTree(v,l,mid,w,nl,mid,lch[now-n]);
 34             addTree(v,mid+1,r,w,mid+1,nr,lch[now-n]+1);
 35         }
 36     }
 37 }
 38 void Treeadd(int v,int l,int r,long long w,int nl,int nr,int now){
 39     if(nl>=l&&nr<=r)add(now,v,w);
 40     else{
 41         int mid=(nl+nr)>>1;
 42         if(r<=mid)Treeadd(v,l,r,w,nl,mid,lch[now-n]);
 43         else if(l>mid)Treeadd(v,l,r,w,mid+1,nr,lch[now-n]+1);
 44         else{
 45             Treeadd(v,l,mid,w,nl,mid,lch[now-n]);
 46             Treeadd(v,mid+1,r,w,mid+1,nr,lch[now-n]+1);
 47         }
 48     }
 49 }
 50 void buildTreest(int now,int l,int r){
 51     if(l==r){
 52         add(now,l,0);
 53         return;
 54     };
 55     int mid=(l+r)>>1;
 56     add(now,++nodetot,0);
 57     lch[now-n]=nodetot;
 58     add(now,++nodetot,0);
 59     buildTreest(lch[now-n],l,mid);
 60     buildTreest(lch[now-n]+1,mid+1,r);
 61 }
 62 void buildTreeet(int now,int l,int r){
 63     if(l==r){
 64         add(l,now,0);
 65         return;
 66     };
 67     int mid=(l+r)>>1;
 68     add(++nodetot,now,0);
 69     lch[now-n]=nodetot;
 70     add(++nodetot,now,0);
 71     buildTreeet(lch[now-n],l,mid);
 72     buildTreeet(lch[now-n]+1,mid+1,r);
 73 }
 74 void Dijkstra(int st){
 75     dist[st]=0;
 76     now=st;
 77     q.push(make_pair(0,st));
 78     while(!q.empty()){
 79         vis[now]=1;
 80         pair<long long,int> x=q.top();
 81         q.pop();
 82         now=x.second;
 83         for(int i=h[now];~i;i=e[i].next){
 84             if(dist[e[i].to]>dist[now]+e[i].w){
 85                 dist[e[i].to]=dist[now]+e[i].w;
 86                 q.push(make_pair(-dist[e[i].to],e[i].to));
 87             }
 88         }
 89     }
 90 }
 91 int main(){
 92     memset(h,-1,sizeof(h));
 93     memset(dist,0x7f,sizeof(dist));
 94     n=read();
 95     eq=read();
 96     s=read();
 97     nodetot=n+1;
 98     buildTreest(n+1,1,n);
 99     buildTreeet(++nodetot,1,n);
100     for(int i=1;i<=eq;i++){
101         Tsk=read();
102         if(Tsk==1){
103             v=read();
104             u=read();
105             w=read();
106             add(v,u,w);
107         }else if(Tsk==2){
108             v=read();
109             l=read();
110             r=read();
111             w=read();
112             addTree(v,l,r,w,1,n,n+1);
113         }else{
114             v=read();
115             l=read();
116             r=read();
117             w=read();
118             Treeadd(v,l,r,w,1,n,n*3);
119         }
120     }
121     Dijkstra(s);
122     for(int i=1;i<=n-1;i++)printf("%lld ",(dist[i]==0x7f7f7f7f7f7f7f7f)?-1:dist[i]);
123     printf("%lld\n",(dist[n]==0x7f7f7f7f7f7f7f7f)?-1:dist[n]);
124 }
时间: 2024-10-24 13:59:57

[CF787D]遗产(Legacy)-线段树-优化Dijkstra(内含数据生成器)的相关文章

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.

【bzoj3073】[Pa2011]Journeys 线段树优化建图+堆优化Dijkstra

题目描述 Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a,b),(c,d)表示,对于任意两个国家x,y,如果a<=x<=b,c<=y<=d,那么在xy之间建造一条道路.Seter保证不会有一个国家与自己之间有道路. Seter好不容易建好了所有道路,他现在在位于P号的首都.Seter想知道P号国家到任意一个国家最少需要经过几条道路.当然,Se

CF786B Legacy 线段树优化建图

问题描述 CF786B LG-CF786B 题解 线段树优化建图 线段树的一个区间结点代表 \([l,r]\) 区间点. 然后建立区间点的时候就在线段树上建边,有效减少点的个数,从而提高时空效率. 优质题解传送门 \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; #define int long long template <typename Tp> void read(Tp &x){ x=0;ch

[Poi2010]Monotonicity 2 (线段树优化DP)

题目描述 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k].选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1].求出L的最大值. 输入 第一行两个正整数,分别表示N和K (N, K <= 500,000).第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6).第三行给出K个空格隔开关系符号(>.<或=),第i个表示s[i]. 输出 一个正整数,表示L的

HDU4719-Oh My Holy FFF(DP线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 606    Accepted Submission(s): 141 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

hdu 4719 Oh My Holy FFF(dp线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 848    Accepted Submission(s): 219 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

题解 HDU 3698 Let the light guide us Dp + 线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 253 Problem Description Plain of despair was

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=

【bzoj4276】[ONTAK2015]Bajtman i Okr?g?y Robin 线段树优化建图+费用流

题目描述 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢走c[i]元.作为保安,你在每一段长度为1的时间内最多只能制止一个强盗,那么你最多可以挽回多少损失呢? 输入 第一行包含一个正整数n(1<=n<=5000),表示强盗的个数. 接下来n行,每行包含三个正整数a[i],b[i],c[i](1<=a[i]<b[i]<=5000,1<=c[i]