1. 程式人生 > >Luogu P5168 xtq玩魔塔

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;
}