【计蒜客】百度科学家(困难)
【题目】百度科学家(困难)
【题意】给定n个非负整数,最终需要选择一个数字集合。m次操作,修改一个非负整数,或规定选择第x个数字则必须选择区间[l,r]内的数字。最终求非空数字集合的最小值。\(n,m \leq 10^5\)。
需要特别注意,被替换了的非负整数也是可以选择的。每一个非负整数都是本质不同的,区间[l,r]内的数字指的是当前情况下区间[l,r]内的数字。
【算法】主席树优化建图+Tarjan缩点
【题解】考虑朴素做法,每次规定点x向区间内的点连边,修改就建新的点替换进序列,最后Tarjan缩点后求出度为0的点权最小值。这样边数是\(O(nm)\)的。
因为向区间连边很容易想到用主席树优化。每次修改在原来的基础上新增一条链,父亲向儿子连边。每次规定用当前最后一个在位置x的点编号向区间连边。
复杂度\(O(n \ \ log \ \ n)\)。
【注意】
1.每次新建链都需要向左右儿子连边。不要连向0。
2.边数组开3倍。
3.有向图的Tarjan算法不用判断反向边。
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
bool isdigit(char c){return c>='0'&&c<='9';}
int read(){
int s=0,t=1;char c;
while(!isdigit(c=getchar()))if(c=='-')t=-1;
do{s=s*10+c-'0';}while(isdigit(c=getchar()));
return s*t;
}
const int maxn=100010,N=1800010;
int dfn[N],low[N],st[N],dfsnum,cnt,tot,first[N],col[N],sz,v[maxn],top,n,m,rt,ou[N];
ll num[N];
struct edge{int u,v,from;}e[N*3];//three
void ins(int u,int v){tot++;e[tot].u=u;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
struct tree{int l,r,s;}t[N];
void insert(int &x,int y,int l,int r,int k,int p){
x=++sz;t[x]=t[y];
if(l==r){t[x].s=p;v[k]=x;return;}
int mid=(l+r)>>1;
if(k<=mid)insert(t[x].l,t[y].l,l,mid,k,p);
else insert(t[x].r,t[y].r,mid+1,r,k,p);
if(t[x].l)ins(x,t[x].l);if(t[x].r)ins(x,t[x].r);//0
}
void tarjan(int x){
dfn[x]=low[x]=++dfsnum;st[++top]=x;
for(int i=first[x];i;i=e[i].from){//fan???
if(!dfn[e[i].v]){
tarjan(e[i].v);
low[x]=min(low[x],low[e[i].v]);
}
else if(!col[e[i].v])low[x]=min(low[x],dfn[e[i].v]);
}
if(dfn[x]==low[x]){
cnt++;
while(st[top]!=x)col[st[top--]]=cnt;
col[st[top--]]=cnt;
}
}
void modify(int k,int l,int r,int L,int R,int x){
if(L<=l&&r<=R)return void(ins(x,k));
int mid=(l+r)>>1;
if(L<=mid)modify(t[k].l,l,mid,L,R,x);
if(R>mid)modify(t[k].r,mid+1,r,L,R,x);
}
int main(){
n=read();
for(int i=1;i<=n;i++)insert(rt,rt,1,n,i,read());
m=read();
while(m--){
int kind=read();
if(!kind){
int x=read(),y=read();
insert(rt,rt,1,n,x,y);
}
else{
int x=read(),l=read(),r=read();
modify(rt,1,n,l,r,v[x]);
}
}
for(int i=1;i<=sz;i++)if(!dfn[i])tarjan(i);
for(int i=1;i<=sz;i++)num[col[i]]+=t[i].s;//long long
for(int i=1;i<=tot;i++)if(col[e[i].u]!=col[e[i].v])ou[col[e[i].u]]++;
ll ans=1ll<<60;
for(int i=1;i<=cnt;i++)if(!ou[i]&&ans>num[i])ans=num[i];
printf("%lld",ans);
return 0;
}