码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-05-13 00:32:15      阅读:242      评论:0      收藏:0      [点我收藏+]

标签:题目   algo   ==   dig   缩点   max   整数   printf   lse   

【题目】百度科学家(困难)
【题意】给定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;
}

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

标签:题目   algo   ==   dig   缩点   max   整数   printf   lse   

原文地址:https://www.cnblogs.com/onioncyc/p/9030618.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!