BZOJ 2001 [Hnoi2010]City 城市建设 LCT+分治(未成功卡时卡过)

题意:
无向图,求每次修改一条边权值后的最小生成树的边权和。


解析:
网上题解都是些什么CDQ重构图的鬼畜算法。
wyf大爷提出了用LCT以及分治解决这道题的办法。
整个时间看做一个轴的话。
那么每条边的颜色必然是几段连续的区间。
所以我们可以处理出来每条边在某时间的颜色是什么。
之后参见4025的分治做法。
在递归的时候开栈记录做了什么删边加边操作,只要逆回去就可以了。
其实可以看做是对于时间轴的一棵线段树。
对于每一个叶子节点,其实就是把所有他到根节点路径上的边加进去即可。
回溯再撤销之前的操作即可。
总共的区间个数大概在O(Qloglogn)左右。
虽然这个玩意的复杂度是对的。
但是由于逆操作以及操作时的次数过多,所以常数非常大。
这题即需要继续进一步卡常数。
然而我并没有卡过去。
但是这是个不鬼畜的思路:)


代码:

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 71000
#define M 51000
#define len 1<<15
using namespace std;
typedef pair<int,int> pa;
typedef long long ll;
bool isdigit(char ch)
{
    return ch>='0'&&ch<='9';
}
char getc()
{
    static char *S,*T,buf[len];
    if(S==T)
    {
        T=(S=buf)+fread(buf,1,len,stdin);
        if(S==T)return EOF;
    }
    return *S++;
}
int read()
{
    static char ch;
    static int d;
    while(!isdigit(ch=getc()));
    for(d=ch-'0';isdigit(ch=getc());)
        d=(d<<1)+(d<<3)+ch-'0';
    return d;
}
struct Que
{
    int st,ed,no;
    int val;
    Que(){}
    Que(int _st,int _ed,int _no,int _val):st(_st),ed(_ed),no(_no),val(_val){} 
};
vector <Que> query;
struct Line
{
    int u,v,no;
    int val;
    Line(){}
    Line(int _u,int _v,int _val,int _no):u(_u),v(_v),val(_val),no(_no){} 
}line[M];
int n,m,q;
int ch[N][2],fa[N],maxnode[N];
int val[N],mx[N];
bool is_rt[N],rev[N];
inline void init()
{
    memset(is_rt,true,sizeof(is_rt));
}
inline void pushup(int rt)
{
    if(!rt)return;
    maxnode[rt]=rt;
    mx[rt]=val[rt];
    if(ch[rt][0]!=0&&mx[ch[rt][0]]>mx[rt])
        mx[rt]=mx[ch[rt][0]],maxnode[rt]=maxnode[ch[rt][0]];
    if(ch[rt][1]!=0&&mx[ch[rt][1]]>mx[rt])
        mx[rt]=mx[ch[rt][1]],maxnode[rt]=maxnode[ch[rt][1]];
}
inline void update_rev(int rt)
{
    if(!rt)return;
    swap(ch[rt][0],ch[rt][1]);
    rev[rt]^=true;
}
inline void pushdown(int rt)
{
    if(!rt)return;
    if(rev[rt])
    {
        update_rev(ch[rt][0]),update_rev(ch[rt][1]);
        rev[rt]^=true;
    }
}
inline void down(int rt)
{
    if(!is_rt[rt])down(fa[rt]);
    pushdown(rt);
}
inline void rotate(int rt,int kind)
{
    int y=fa[rt];
    ch[y][kind]=ch[rt][!kind];
    fa[ch[y][kind]]=y;
    ch[rt][!kind]=y;
    fa[rt]=fa[y];
    fa[y]=rt;
    if(is_rt[y])
        is_rt[y]=false,is_rt[rt]=true;
    else ch[fa[rt]][ch[fa[rt]][1]==y]=rt;
    pushup(y);
}
inline void splay(int rt)
{
    down(rt);
    while(!is_rt[rt])
    {
        int y=fa[rt],kind=(ch[y][1]==rt);
        if(is_rt[y])rotate(rt,kind);
        else if((ch[fa[y]][1]==y)==kind)
            rotate(y,kind),rotate(rt,kind);
        else rotate(rt,kind),rotate(rt,!kind);
    }
    pushup(rt);
}
inline void access(int rt)
{
    int y=0;
    while(rt)
    {
        splay(rt);
        is_rt[ch[rt][1]]=true;
        is_rt[y]=false;
        ch[rt][1]=y;
        pushup(rt);
        y=rt,rt=fa[rt];
    }
}
inline void movetoroot(int rt){access(rt);splay(rt);update_rev(rt);}
inline void link(int x,int y){movetoroot(x);fa[x]=y;} 
inline void cut(int x,int y){movetoroot(x);access(y);splay(y);ch[y][0]=0,fa[x]=0,is_rt[x]=true;}
inline bool findroot(int x,int y){movetoroot(x),access(y),splay(y);while(ch[x][0])x=ch[x][0];while(ch[y][0])y=ch[y][0];return x==y;}
int pre[M];
int preval[M];
ll ans;
struct Stack
{
    int no,val;
    Stack(){}
    Stack(int _no,int _val):no(_no),val(_val){}
}sta[M<<3];
int top;
inline void Restore(int bot)
{
    while(top>bot)
    {
        if(sta[top].no<0)
        {
            cut(line[-sta[top].no].u,-sta[top].no+n);
            cut(-sta[top].no+n,line[-sta[top].no].v);
            ans-=sta[top].val;
        }else
        {
            val[sta[top].no+n]=mx[sta[top].no+n]=sta[top].val;
            maxnode[sta[top].no+n]=sta[top].no+n;
            link(line[sta[top].no].u,sta[top].no+n),link(sta[top].no+n,line[sta[top].no].v);
            ans+=sta[top].val;
        }
        top--;
    }
}
inline void Divide_and_Conquer(int L,int R,vector<Que> &ve)
{
    int mid=(L+R)>>1;
    vector<Que>::iterator it;
    vector<Que>l,r;
    int bot=top;
    for(it=ve.begin();it!=ve.end();it++)
    {
        if(it->st==L&&it->ed==R)
        {
            int x=line[it->no].u,y=line[it->no].v;
            if(findroot(x,y))
            {
                ll tmpval=mx[y];
                if(it->val>=tmpval)continue;
                else
                {
                    int tmpnode=maxnode[y];
                    cut(line[tmpnode-n].u,tmpnode),cut(tmpnode,line[tmpnode-n].v);
                    val[it->no+n]=mx[it->no+n]=it->val;
                    maxnode[it->no+n]=it->no+n;
                    link(x,it->no+n),link(it->no+n,y);
                    ans=ans-tmpval+it->val;
                    sta[++top]=Stack(tmpnode-n,tmpval);
                    sta[++top]=Stack(-(it->no),it->val);
                }
            }else
            {
                val[it->no+n]=mx[it->no+n]=it->val;
                maxnode[it->no+n]=it->no+n;
                ans+=it->val;
                link(x,it->no+n),link(it->no+n,y);
                sta[++top]=Stack(-(it->no),it->val);
            }
        }else if(it->ed<=mid)
            l.push_back(*it);
         else if(it->st>mid)
            r.push_back(*it);
         else l.push_back(Que(it->st,mid,it->no,it->val)),r.push_back(Que(mid+1,it->ed,it->no,it->val));
    }
    if(L==R)
    {
        printf("%lld\n",ans);
        Restore(bot);
        return;
    }
    Divide_and_Conquer(L,mid,l);
    Divide_and_Conquer(mid+1,R,r);
    Restore(bot);
}
int main()
{
    int i,x,y,z,no,val;
    init();
    n=read(),m=read(),q=read();
    for(i=1;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        line[i]=Line(x,y,z,i);
        pre[i]=1,preval[i]=z;
    }
    for(i=1;i<=q;i++)
    {
        no=read(),val=read();
        int L=pre[no],R=i-1;
        if(L>R)pre[no]=i,preval[no]=val;
        else
        {
            query.push_back(Que(L,R,no,preval[no]));
            pre[no]=i,preval[no]=val;
        }
    }
    for(int i=1;i<=m;i++)
        query.push_back(Que(pre[i],q,i,preval[i]));
    Divide_and_Conquer(1,q,query);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值