【2019北京集训2】duck 线段树优化建图+tarjan

题目大意:给你$n$个点,第$i$个点有点权$v_i$。你需要将这$n$个点排成一排,第$i$个点的点权能被累加当且仅当这个点前面存在编号在$[l_i,r_i]$中的点,问你这些点应该如何排列,点权和才能最大。

数据范围:$n≤10^5$,$1≤v_i≤10^4$。

这题状压居然给了70分,场上压根没想正解。

我们不难发现,对于点i,我们连接$l_i→i$,$(l_i+1)→i$,....,$r_i→i$的边,然后跑一个tarjan,缩点后我们得到了一棵树。

对于每棵树,我们显然只需要减去这棵树树根中最小的点权即可。

然后这么做显然是$O(n^2)$的,考虑优化一波

不难发现,这里连边是连向一个区间,我们可以用线段树优化连边,就可以把连边数量降低至log级。

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

 1 #include<bits/stdc++.h>
 2 #define M 400005
 3 using namespace std;
 4
 5 struct edge{int u,next;}e[M*30]={0}; int head[M]={0},use=0;
 6 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
 7 int val[M]={0},n;
 8
 9 int dfn[M]={0},low[M]={0},b[M]={0},d[M]={0},sum[M]={0},minn[M]={0},t=0,cnt=0; stack<int> s;
10
11 struct seg{int l,r,id;}a[M<<2]={0};
12 int id[M]={0},all=0;
13 int build(int x,int l,int r){
14     a[x].l=l; a[x].r=r;
15     if(l==r) return a[x].id=id[l]=++all;
16     int mid=(l+r)>>1;
17     build(x<<1,l,mid);
18     build(x<<1|1,mid+1,r);
19 }
20 int build2(int x,int l,int r){
21     if(l==r) return 0;
22     int mid=(l+r)>>1;
23     build2(x<<1,l,mid);
24     build2(x<<1|1,mid+1,r);
25     a[x].id=++all;
26     add(a[x<<1].id,a[x].id);
27     add(a[x<<1|1].id,a[x].id);
28 }
29
30 void updata(int x,int l,int r,int ID){
31     if(l<=a[x].l&&a[x].r<=r){
32         add(a[x].id,ID);
33         return;
34     }
35     int mid=(a[x].l+a[x].r)>>1;
36     if(l<=mid) updata(x<<1,l,r,ID);
37     if(mid<r) updata(x<<1|1,l,r,ID);
38 }
39
40 void dfs(int x){
41     dfn[x]=low[x]=++t; b[x]=1; s.push(x);
42     for(int i=head[x];i;i=e[i].next)
43     if(!dfn[e[i].u]) dfs(e[i].u),low[x]=min(low[x],low[e[i].u]);
44     else if(b[e[i].u]) low[x]=min(low[x],dfn[e[i].u]);
45     if(dfn[x]==low[x]){
46         int u; cnt++;
47         do{
48             u=s.top(); s.pop();
49             d[u]=cnt; b[u]=0;
50             if(val[u]!=val[0]){
51                 sum[cnt]+=val[u]; minn[cnt]=min(minn[cnt],val[u]);
52             }
53         }while(u!=x);
54     }
55 }
56
57 int main(){
58     memset(minn,1,sizeof(minn));
59     memset(val,1,sizeof(val));
60     scanf("%d",&n);
61     build(1,1,n);
62     build2(1,1,n);
63     for(int i=1;i<=n;i++){
64         int l,r; scanf("%d%d%d",&l,&r,val+i);
65         updata(1,l,r,id[i]);
66     }
67     for(int i=1;i<=all;i++) if(!dfn[i]) dfs(i);
68     for(int x=1;x<=all;x++)
69     for(int i=head[x];i;i=e[i].next)
70     if(d[e[i].u]!=d[x]) ++b[d[e[i].u]];
71     int ans=0;
72     for(int i=1;i<=cnt;i++){
73         ans+=sum[i];
74         if(!b[i]) ans-=minn[i];
75     }
76     cout<<ans<<endl;
77 }

原文地址:https://www.cnblogs.com/xiefengze1/p/10646589.html

时间: 2024-10-10 13:47:16

【2019北京集训2】duck 线段树优化建图+tarjan的相关文章

bzoj5017 [Snoi2017]炸弹 (线段树优化建图+)tarjan 缩点+拓扑排序

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5017 题解 这个题目方法挺多的. 线段树优化建图 线段树优化建图的做法应该挺显然的,一个炸弹能够引爆的炸弹的显然应该是一个区间里面的,直接对这个区间进行线段树优化建图. 这样可以得到一个带环图,缩点以后这个炸弹能够炸到的炸弹就是从这个点能够走到的点. 但是这个不太好做,不过可以发现最终的炸弹也是一个区间,所以可以通过缩点后的 DAG 来求出左右端点. 时间复杂度 \(O(n\log n)\)

模拟赛T2 线段树优化建图+tarjan+拓扑排序

然而这只是 70pts 的部分分,考场上没想到满分怎么做(现在也不会) code: #include <cstdio> #include <string> #include <stack> #include <queue> #include <cstring> #include <algorithm> #define N 100009 #define lson ls[x] #define rson rs[x] #define inf

【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]

【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

【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流

[BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一天,她准备去探访他.对着窗外的阳光,临行前她再次弹起了琴.她的琴的发声十分特殊.让我们给一个形式化的定义吧.所有的 n 个音符形成一棵由音符 C ( 1 号节点) 构成的有根树,每一个音符有一个音高 Hi .Arietta 有 m 个力度,第 i 个力度能弹出 Di 节点的子树中,音高在 [Li,R

【ARC069F】Flags 2-sat+线段树优化建图+二分

Description ? 数轴上有 n 个旗子,第 ii 个可以插在坐标 xi或者 yi,最大化两两旗子之间的最小距离. Input ? 第一行一个整数 N. ? 接下来 N 行每行两个整数 xi,yi Output ? 一个整数表示答案. Sample Input Sample #1 3 1 3 2 5 1 9 Sample #2 5 2 2 2 2 2 2 2 2 2 2 Sample #3 22 93 6440 78 6647 862 11 8306 9689 798 99 801 52

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.

一个神秘的oj2587 你猜是不是dp(线段树优化建图)

哇 这难道不是happiness的翻版题嘛? 从\(S\)向一个点连染成白色的收益 从这个点向\(T\)连染成黑色的收益 对于额外的收益,建一个辅助点,跟区间内的每个点连\(inf\),然后向S/T,连流量为收益 这不就结束了吗? 自信写完,提交 woc!!只有40分? #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath>

机房测试5:reverse(bfs+set 或 线段树优化建图)

题目: 分析: 首先画样例分析一下,会发现如果要求一个位置要多少次翻转,就将这个位置向与它关联的点连边(关联点指的是可能与它值互换的位置),一直连到起点为止,连边的次数即为它所需步数. 所以转换成求单源最短路,因为边权为1,可以用bfs. 但是这道题n的范围很大,刚刚的做法是n*k的,考虑优化. 法1:在建图上优化 题目要求的是区间翻转,所以也对应着相关性质:每个点连边一定是都连的奇数点或偶数点(画图可知),且这些奇数偶数点都对应着一段连续的区间. 如果可以将点向点连边优化成点向区间连边,复杂度