【计蒜客】百度科学家(困难)

【题目】百度科学家(困难)
【题意】给定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;
}
posted @ 2018-05-12 23:43  ONION_CYC  阅读(459)  评论(0编辑  收藏  举报