• 7377. 【2021.11.11NOIP提高组联考】欢乐豆


    Description

    您有一个有向完全图,其中 \(u \rightarrow v\) 的边权 \(a_{u}\)

    将会有 \(m\) 次修改,第 \(i\) 次修改会给定 \(x, y, z\) ,并将 \(x \rightarrow y\)有向边的边权改为 \(z\)

    求所有点对 \(i, j(i \neq j)\) 间最短路之和。

    \(n\le 10^5,m\le 3000\)

    Solution

    注意到更改最多只有 3000 次,意味着将会有很多点未被更改,我们记这些没有被更改过的点(无论是连出去还是连进来)为普通点。那么对于任意两个普通点之间的距离是很容易算的。

    现在考虑特殊点,将所有特殊点放进一个队列,同时将 \(a_i\) 最小的普通点也放进队列。但是 \(a_i\) 本身的答案已经计算过了,这里是因为考虑到存在特殊点 \(\rightarrow\) 普通点 \(\rightarrow\) 特殊点的特殊情况。

    遍历这个序列,以每个特殊点为起点跑 \(\text{dij}\),时间复杂度 \(\mathcal O(n^3)\)

    考虑优化,由于总边数不大,那么对于每个特殊点,连出去的特殊边不会特别多,因此可以采取线段树优化。对于起点这个特殊点,找出与它有连边的特殊点,那么两个特殊点之间的贡献是相同的,可以区间修改,然后对于有连边的特殊点单点修改即可。优化到 \(\mathcal O(n^2\log{n})\)

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define inf 1145141919810
    #define ll long long
    using namespace std;
    struct node
    {
        int to,next,head;
        ll val;
    }edge[N<<1],son[N];
    struct seg
    {
        int id;
        ll lazy,mn;
    }tree[N<<2];
    int n,m,cnt,tot,flag,a[N],x[N],y[N],z[N],id[N],c[N];
    ll ans;
    bool bj[N];
    void add(int x,int y,int z)
    {
        edge[++tot].to=y;
        edge[tot].val=z;
        edge[tot].next=edge[x].head;
        edge[x].head=tot;
    }
    bool cmp(node x,node y) {return x.to<y.to;}
    void pushdown(int x)
    {
        if (!bj[x<<1])
        {
            tree[x<<1].mn=min(tree[x<<1].mn,tree[x].lazy);
            if (tree[x<<1].lazy!=-1) tree[x<<1].lazy=min(tree[x<<1].lazy,tree[x].lazy);
            else tree[x<<1].lazy=tree[x].lazy;
        }
        if (!bj[x<<1|1])
        {
            tree[x<<1|1].mn=min(tree[x<<1|1].mn,tree[x].lazy);
            if (tree[x<<1|1].lazy!=-1) tree[x<<1|1].lazy=min(tree[x<<1|1].lazy,tree[x].lazy);
            else tree[x<<1|1].lazy=tree[x].lazy;
        }
    }
    void pushup(int x)
    {
        ll lz=tree[x].lazy;
        if (bj[x<<1]&&bj[x<<1|1]) bj[x]=true;
        if ((tree[x<<1].mn<tree[x<<1|1].mn&&!bj[x<<1])||bj[x<<1|1]) tree[x]=tree[x<<1];
        else tree[x]=tree[x<<1|1];
        tree[x].lazy=lz;
    }
    void build(int now,int l,int r)
    {
        tree[now].id=l;
        tree[now].lazy=-1;
        tree[now].mn=inf;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(now<<1,l,mid);build(now<<1|1,mid+1,r);
    }
    void modify(int now,int l,int r,int p,int q,ll v)
    {
        if (tree[now].lazy!=-1)
        {
            if (tree[now].lazy<=v) return;
            else pushdown(now);
        }
        if (l>=p&&r<=q)
        {
            if (tree[now].lazy==-1) tree[now].lazy=v;
            else tree[now].lazy=min(tree[now].lazy,v);
            tree[now].mn=min(tree[now].mn,v);
            return;
        }
        int mid=(l+r)>>1;
        if (p<=mid&&q>=l&&!bj[now<<1]) modify(now<<1,l,mid,p,q,v);
        if (p<=r&&q>mid&&!bj[now<<1|1]) modify(now<<1|1,mid+1,r,p,q,v);
        pushup(now);
    }
    ll query(int now,int l,int r)
    {
        if (l==r)
        {
            if (r==cnt&&flag) return tree[now].mn*(ll)flag;
            else return tree[now].mn;
        }
        int mid=(l+r)>>1;
        return query(now<<1,l,mid)+query(now<<1|1,mid+1,r);
    }
    void mark(int now,int l,int r,int p)
    {
        if (tree[now].lazy!=-1) pushdown(now);
        if (l==r)
        {
            bj[now]=1;
            return;
        }
        int mid=(l+r)>>1;
        if (p<=mid) mark(now<<1,l,mid,p);
        else mark(now<<1|1,mid+1,r,p);
        pushup(now);
    }
    void dij(int x)
    {
        memset(bj,false,sizeof(bj));
        build(1,1,cnt);
        modify(1,1,cnt,x,x,0);
        int t=0;
        while (t<cnt)
        {
            ll res=tree[1].mn;
            int now=tree[1].id,num=0;
            mark(1,1,cnt,now);
            for (int i=edge[now].head;i;i=edge[i].next)
            {
                son[++num].to=edge[i].to;
                son[num].val=edge[i].val;
            }
            sort(son+1,son+num+1,cmp);
            for (int i=1;i<=num;++i)
            {
                modify(1,1,cnt,son[i-1].to+1,son[i].to-1,res+c[now]);
                modify(1,1,cnt,son[i].to,son[i].to,res+son[i].val);
            }
            if (son[num].to+1<=cnt) modify(1,1,cnt,son[num].to+1,cnt,res+c[now]);
            ++t;
        }
        ans+=query(1,1,cnt);
    }
    int main()
    {
        freopen("happybean.in","r",stdin);
        freopen("happybean.out","w",stdout);
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;++i)
            scanf("%d",&a[i]);
        for (int i=1;i<=m;++i)
        {
            scanf("%d%d%d",&x[i],&y[i],&z[i]);
            bj[x[i]]=bj[y[i]]=true;
        }
        for (int i=1;i<=n;++i)
            if (bj[i]) id[i]=++cnt,c[cnt]=a[i];
        for (int i=1;i<=m;++i)
            add(id[x[i]],id[y[i]],z[i]);
        if (cnt!=n)
        {
            ++cnt;
            int k=0;
            for (int i=1;i<=n;++i)
                if (!bj[i])
                {
                    id[i]=cnt;
                    if (a[i]<a[k]||!k) k=i;
                    ans+=a[i];
                    flag++;
                }
            c[cnt]=a[k];
            ans=(ll)ans*(n-1);
        }
        for (int i=1;i<cnt;++i)
            dij(i);
        if (!flag) dij(cnt);
        printf("%lld\n",ans);
        return 0;
    }
    
  • 相关阅读:
    在.wxml文件中使用方法
    Element日期区间选择器限制选择范围
    规定段落中的文本不进行换行
    文件大小换算
    elementUI的message消息提示改成只能同时存在一个
    SQL 执行顺序
    SQL Left join
    wpf datagrid 行双击事件
    关于Infragistics.WebUI.UltraWebGrid的使用
    ASP.NET 根据汉字获取汉字拼音的首字母(含多音字)
  • 原文地址:https://www.cnblogs.com/Livingston/p/15577307.html
Copyright © 2020-2023  润新知