Luogu P5168 xtq玩魔塔

这题不错啊,结合了一些不太传统的姿势。

首先看到题目有一问从一个点到另一个点边权最小值。想到了什么?

克鲁斯卡尔生成树+倍增?好吧其实有一个更常用NB的算法叫克鲁斯卡尔重构树

(不会的可以看dalao's blog,并且可以尝试切掉Luogu P4768 [NOI2018]归程

回到这题,我们可以把重构树建出来之后直接求两点LCA的权值。

然后对于第三问,考虑继续利用重构树,我们发现此时能走到的点在树上一定是一颗子树。

子树内DFS序连续啊,所以就变成区间数颜色了,直接莫队啊!

好吧还有修改,那就带修莫队,在数据随机的情况下稳如老狗。

然后这题就完了,不过有一个细节就是克鲁斯卡尔重构树的父节点权值一定大于子节点,所以不用在向上跳的时候再维护一个最大值数组。

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#define RI register int
#define Tp template <typename T>
using namespace std;
const int N=100005;
struct data
{
    int x,y,val;
    inline friend bool operator <(data A,data B)
    {
        return A.val<B.val;
    }
}a[N*3]; int n,m,s,opt,x,y,z,ans[N<<1],rst[N*3],dfn[N<<1],blk[N];
struct ques
{
    int l,r,id,t;
    inline ques (int L=0,int R=0,int Id=0,int T=0)
    {
        l=L; r=R; id=Id; t=T;
    }
    inline friend bool operator <(ques A,ques B)
    {
        return blk[A.l]!=blk[B.l]?blk[A.l]<blk[B.l]:(blk[A.r]!=blk[B.r]?blk[A.r]<blk[B.r]:A.t<B.t);
    }
}q[N<<1]; int cnt_q,cnt_cm,cnt_col,bkt[N*3],tot,d[N<<1],sze[N<<1];
struct operation
{
    int pos,col;
    inline operation(int Pos=0,int Col=0) { pos=Pos; col=Col; }
}p[N<<1]; int cnt_p,col[N],now,L=1,R,list[N],ret,size;
class FileInputOutput
{
    private:
        static const int S=1<<21;
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
        #define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
        char Fin[S],Fout[S],*A,*B; int Ftop,pt[15];
    public:
        Tp inline void read(T &x)
        {
            x=0; char ch; while (!isdigit(ch=tc()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
        }
        Tp inline void write(T x)
        {
            if (!x) return (void)(pc('0'),pc('\n')); RI ptop=0;
            while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
        }
        inline void Fend(void)
        {
            fwrite(Fout,1,Ftop,stdout);
        }
}F;
inline void swap(int &x,int &y)
{
    int t=x; x=y; y=t;
}
class Double_Increased_On_Tree
{
    private:
        static const int P=18;
        struct edge
        {
            int to,nxt;
        }e[N<<1]; int head[N<<1],cnt,idx,anc[N<<1][P],dep[N<<1];
        inline void reset(int now)
        {
            for (RI i=0;i<P-1;++i) if (anc[now][i])
            anc[now][i+1]=anc[anc[now][i]][i]; else break;
        }
        inline void miner(int &x,int y)
        {
            if (y<x) x=y;
        }
    public:
        inline void add(int x,int y)
        {
            e[++cnt]=(edge){y,head[x]},head[x]=cnt;
        }
        #define to e[i].to
        inline void DFS(int now)
        {
            if (now<=n) list[dfn[now]=++idx]=now,sze[now]=1;
            else dfn[now]=1e9;reset(now); for (RI i=head[now];i;i=e[i].nxt)
            anc[to][0]=now,dep[to]=dep[now]+1,DFS(to),sze[now]+=sze[to],miner(dfn[now],dfn[to]);
        }
        #undef to
        inline int getLCA(int x,int y)
        {
            RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
            if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
            for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
            x=anc[x][i],y=anc[y][i]; return anc[x][0];
        }
        inline int getinterval(int x,int y)
        {
            for (RI i=P-1;~i;--i)if (anc[x][i]&&d[anc[x][i]]<=y) x=anc[x][i]; return x;
        }
}T;
class Kruskal_Rubuild_Tree_Solver
{
    private:
        int father[N<<1];
        inline int getfather(int x)
        {
            return father[x]^x?father[x]=getfather(father[x]):x;
        }
    public:
        inline void init(void)
        {
            for (RI i=1;i<=n;++i) father[i]=i;
        }
        inline void Kruskal(void)
        {
            sort(a+1,a+m+1); init(); for (RI i=1;i<=m;++i)
            if ((a[i].x=getfather(a[i].x))!=(a[i].y=getfather(a[i].y)))
            {
                d[++tot]=a[i].val; father[a[i].x]=father[a[i].y]=tot;
                T.add(tot,a[i].x); T.add(tot,a[i].y); father[tot]=tot;
            }
        }
}K;
inline void add(int col)
{
    if (++bkt[col]==1) ++ret;
}
inline void del(int col)
{
    if (--bkt[col]==0) --ret;
}
inline void travel(int now)
{
    if (p[now].pos>=L&&p[now].pos<=R) del(col[list[p[now].pos]]),
    add(p[now].col); swap(p[now].col,col[list[p[now].pos]]);
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    RI i; for (F.read(n),F.read(m),F.read(s),i=1;i<=n;++i)
    F.read(col[i]),rst[++cnt_col]=col[i]; for (i=1;i<=m;++i)
    F.read(a[i].x),F.read(a[i].y),F.read(a[i].val);
    for (tot=n,K.Kruskal(),T.DFS(tot),i=1;i<=s;++i)
    {
        F.read(opt); F.read(x); F.read(y);
        switch (opt)
        {
            case 1:
                p[++cnt_p]=operation(dfn[x],y); rst[++cnt_col]=y; break;
            case 2:
                ans[++cnt_q]=d[T.getLCA(x,y)]; break;
            case 3:
                int top=T.getinterval(x,y);	q[++cnt_cm]=ques(dfn[top],dfn[top]+sze[top]-1,++cnt_q,cnt_p); break;
        }
    }
    sort(rst+1,rst+cnt_col+1); cnt_col=unique(rst+1,rst+cnt_col+1)-rst-1;
    for (i=1;i<=n;++i) col[i]=lower_bound(rst+1,rst+cnt_col+1,col[i])-rst;
    for (i=1;i<=cnt_p;++i) p[i].col=lower_bound(rst+1,rst+cnt_col+1,p[i].col)-rst;
    for (size=(int)pow(n,2.0/3.0),i=1;i<=n;++i) blk[i]=(i-1)/size+1;
    for (sort(q+1,q+cnt_cm+1),i=1;i<=cnt_cm;++i)
    {
        while (now<q[i].t) travel(++now); while (now>q[i].t) travel(now--);
        while (L>q[i].l) add(col[list[--L]]); while (R<q[i].r) add(col[list[++R]]);
        while (L<q[i].l) del(col[list[L++]]); while (R>q[i].r) del(col[list[R--]]);
        ans[q[i].id]=ret;
    }
    for (i=1;i<=cnt_q;++i) F.write(ans[i]); return F.Fend(),0;
}
posted @ 2019-01-07 19:59  空気力学の詩  阅读(161)  评论(0编辑  收藏  举报