Duck - 线段树优化建图 - 强连通分量

题目大意:给定n个区间[li,ri],求一个排列p,元素x若满足至少有一个[lx,rx]中的元素y严格在x前面,就会获得vx的收益。问最大收益。n<=2e5.
题解:考虑x向[lx,rx]连边,那么对于不同的强连通分量,总是能做到排在前面的强连通分量内的点全部获得收益;没有出边的强连通分量总是至少存在一个点没有办法获得收益,钦定是那个最小的点即可。线段树优化建图一下。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
namespace INPUT_SPACE{
    const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;inline int gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
    inline int inn() { int x,ch;while((ch=gc())<'0'||ch>'9');x=ch^'0';while((ch=gc())>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return x; }
}using INPUT_SPACE::inn;
const int N=3000000,M=5000000,INF=INT_MAX;
struct edges{
    int to,pre;
}e[M];int h[N],etop,v[N],lc[N],rc[N],mn[N],sta[N],dfn[N],low[N],bel[N],out[N],nc,dfc,scc;stack<int> s;
inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
inline int build(int &rt,int l,int r)
{
    if(!rt) rt=++nc,v[nc]=INF;int mid=(l+r)>>1;if(l==r) return add_edge(rt,l);
    return build(lc[rt],l,mid),add_edge(rt,lc[rt]),build(rc[rt],mid+1,r),add_edge(rt,rc[rt]);
}
inline int add(int rt,int l,int r,int s,int t,int x)
{
    if(s<=l&&r<=t) return add_edge(x,rt);int mid=(l+r)>>1;
    if(s<=mid) add(lc[rt],l,mid,s,t,x);if(mid<t) add(rc[rt],mid+1,r,s,t,x);return 0;
}
inline int tarjan(int x)
{
    low[x]=dfn[x]=++dfc,sta[x]=1,s.push(x);
    for(int i=h[x],y;i;i=e[i].pre)
        if(!sta[y=e[i].to]) tarjan(y),low[x]=min(low[x],low[y]);
        else if(sta[y]==1) low[x]=min(low[x],dfn[y]);
    if(low[x]==dfn[x])
    {
        scc++,mn[scc]=v[x],bel[x]=scc,sta[x]=2;int y;
        while(s.top()^x) y=s.top(),sta[y]=2,bel[y]=scc,mn[scc]=min(mn[scc],v[y]),s.pop();
        s.pop();
    }
    return 0;
}
int main()
{
    int n=inn(),ans=0,rt=0,l,r;nc=n,build(rt,1,n);
    rep(i,1,n) l=inn(),r=inn(),v[i]=inn(),add(rt,1,n,l,r,i),ans+=v[i];
    rep(i,1,nc) if(!sta[i]) tarjan(i);
    rep(x,1,nc) for(int i=h[x];i;i=e[i].pre)
        if(bel[e[i].to]^bel[x]) out[bel[x]]++;
    rep(i,1,scc) if(!out[i]) ans-=mn[i];return !printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值