题目大意:给你$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