[圆方树][树链剖分][set]JZOJ 5909 跑商

Description
题目背景:
尊者神高达很穷,所以他需要跑商来赚钱
题目描述:
基三的地图可以看做 n 个城市,m 条边的无向图,尊者神高达会从任意一个点出发并在起点购买货物,在旅途中任意一点卖出并最终到达终点,尊者神高达的时间很宝贵,所以他不会重复经过同一个城市,但是为了挣钱,他可能会去绕路。当然,由于工作室泛滥,所以一个城市的货物价格可能会发生改变。但是尊者神高达智商不足,他可能在一个很蠢的节点把货物卖掉,所以尊者神高达想知道每一次跑商最多能赔多少钱。
 
Input
第一行 n,m;
接下来 1 行 n 个数,代表每个城市货物的价格;
接下来 m 行 u,v 代表一条边
接下来 1 行 Q
接下来 Q 行
C x w 代表城市 x 的货物价格变为 w
Q u v 代表一次从 u 到 v 的跑商
Output
如题目描述
 
Sample Input
3 3 
1 2 3
1 2
2 3
1 3
3
Q 2 3
C 1 5
Q 1 3
 
Sample Output
1
3

样例解释:
1,2,3 都联通,起点购买价格为 2,在 1 点卖出赔得最多2-1=1
更新后每个点价值为 5,2,3
起点价格为 5,在 2 点卖出赔得最多,5-2=3
 
Data Constraint
40%的数据为一棵树
另外 20%的数据没有修改操作
所以数据满足 n,m,q<=100000;保证图联通,数据合法
 
Hint
Tips:数据很水可以尝试瞎搞算法,1,2 两道题算法我暑假讲过类似的,各位随便 AC,第三题应该有不少大佬见过原题 orz。

分析

原题怪!

这题我们转换成圆方树,然后对于方点,我们储存的值为所连叶子节点的最小值(虽然父亲也是环的一部分但是算进去很麻烦)用set维护

然后树链剖分即可

对于LCA为方点的特殊情况,我们要特判一下它的父亲

 

#include <iostream>
#include <cstdio>
#include <set>
#include <stack>
using namespace std;
const int N=1e6+10;
struct Node {
    int sz,son,top,fa,seg,dep,w;
}t[N];
int rev[N],scnt;
struct Edge {
    int u,v,nx;
}g[4*N],e[2*N];
int cnt,elist[N],list[2*N];
int s[4*N];
int low[N],dfn[N],tme;
stack<int> stk;
multiset<int> sqr[N];
int sqcnt;
int n,m,q;

void Adde(int u,int v) {
    e[++cnt].u=u;e[cnt].v=v;e[cnt].nx=elist[u];elist[u]=cnt;
}

void Add(int u,int v) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].nx=list[u];list[u]=cnt;
}

void Tarjan(int u,int fa) {
    low[u]=dfn[u]=++tme;stk.push(u);
    for (int i=elist[u];i;i=e[i].nx)
        if (e[i].v!=fa)
            if (!dfn[e[i].v]) {
                Tarjan(e[i].v,u);
                if (low[e[i].v]>=dfn[u]) {
                    Add(++sqcnt,u);Add(u,sqcnt);
                    while (!stk.empty()&&stk.top()!=e[i].v) Add(sqcnt,stk.top()),Add(stk.top(),sqcnt),stk.pop();
                    Add(sqcnt,e[i].v),Add(e[i].v,sqcnt),stk.pop();
                }
                else low[u]=min(low[u],low[e[i].v]);
            }
            else low[u]=min(low[u],dfn[e[i].v]);
}

void Dfs1(int u,int fa) {
    t[u].fa=fa;t[u].sz=1;t[u].dep=t[fa].dep+1;
    if (fa>n) sqr[fa-n].insert(t[u].w);
    for (int i=list[u];i;i=g[i].nx)
        if (g[i].v!=fa) {
            Dfs1(g[i].v,u);
            t[u].sz+=t[g[i].v].sz;
            if (t[g[i].v].sz>t[t[u].son].sz) t[u].son=g[i].v;
        }
}

void Dfs2(int u,int fa) {
    int son=t[u].son;
    if (u>n) t[u].w=*sqr[u-n].begin();
    if (son) {
        t[son].seg=++scnt;
        rev[scnt]=son;
        t[son].top=t[u].top;
        Dfs2(son,u);
    }
    for (int i=list[u];i;i=g[i].nx)
        if (!t[g[i].v].top) {
            t[g[i].v].seg=++scnt;
            rev[scnt]=g[i].v;
            t[g[i].v].top=g[i].v;
            Dfs2(g[i].v,u);
        }
}

void Build(int l,int r,int x) {
    if (l==r) {
        s[x]=t[rev[l]].w;
        return;
    }
    int mid=l+r>>1;
    Build(l,mid,x<<1);Build(mid+1,r,(x<<1)+1);
    s[x]=min(s[x<<1],s[(x<<1)+1]);
}

void Change(int l,int r,int w,int k,int x) {
    if (l>k||k>r) return;
    if (l==r&&r==k) {
        s[x]=w;
        return;
    }
    int mid=l+r>>1;    
    if (mid>=k) Change(l,mid,w,k,x<<1);
    if (mid<k) Change(mid+1,r,w,k,(x<<1)+1);
    s[x]=min(s[x<<1],s[(x<<1)+1]);
}

int Query_Seg(int l,int r,int ll,int rr,int x) {
    if (l>rr||ll>r) return 2147483647;
    if (ll<=l&&r<=rr) return s[x];
    int mid=l+r>>1,ans=2147483647;
    if (ll<=mid) ans=Query_Seg(l,mid,ll,rr,x<<1);
    if (mid<rr) ans=min(ans,Query_Seg(mid+1,r,ll,rr,(x<<1)+1));
    return ans;
}

int Query_Tree(int x,int y) {
    int fx=t[x].top,fy=t[y].top;
    int ans=2147483647;
    while (fx!=fy) {
        if (t[fx].dep<t[fy].dep) swap(x,y),swap(fx,fy);
        ans=min(ans,Query_Seg(1,scnt,t[fx].seg,t[x].seg,1));
        x=t[fx].fa;fx=t[x].top;
    }
    if (t[x].dep>t[y].dep) swap(x,y);
    return min(min(ans,x>n?t[t[x].fa].w:2147483647),Query_Seg(1,scnt,t[x].seg,t[y].seg,1));
}

int main() {
    freopen("paoshang.in","r",stdin);
    freopen("paoshang.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&t[i].w);
    for (int i=1;i<=m;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        Adde(u,v);Adde(v,u);
    }
    sqcnt=n;cnt=0;Tarjan(1,0);
    Dfs1(1,0);
    scnt=rev[1]=t[1].top=t[1].seg=1;
    Dfs2(1,0);
    Build(1,scnt,1);
    scanf("%d",&q);
    for (int i=1;i<=q;i++) {
        char c;
        do {
            scanf("%c",&c);
        }
        while (c!='Q'&&c!='C');
        int u,v;
        scanf("%d%d",&u,&v);
        if (c=='Q') printf("%d\n",t[u].w-Query_Tree(u,v));
        else {
            Change(1,scnt,v,t[u].seg,1);
            if (t[u].fa>n) {
                sqr[t[u].fa-n].erase(sqr[t[u].fa-n].find(t[u].w));
                sqr[t[u].fa-n].insert(v);
                int fake=*sqr[t[u].fa-n].begin();
                if (t[t[u].fa].w!=fake) t[t[u].fa].w=fake,Change(1,scnt,fake,t[t[u].fa].seg,1);
            }
            t[u].w=v;
        }
    }
}
View Code

 

转载于:https://www.cnblogs.com/mastervan/p/9813713.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值