In mathematics you don't understand things, you just get used to them.

标签 data structures 下的文章

Description

Link.

给出一个带权无向图,边权为 $2^{a}\cdot3^{b}$ 形式。

给出 $q$ 组形如 $u,v,a,b$ 的询问,问 $u,v$ 中是否存在一条路径使得其边权之 $\text{lcm}$ 为 $2^{a}\cdot3^{b}$。

Solution

考虑 $\text{lcm}$ 的本质,对 $n$ 个数 $\prod_{i=1}^{k_{1}}p_{1,i}^{c_{1,i}},\prod_{i=1}^{k_{2}}p_{2,i}^{c_{2,i}},\dots,\prod_{i=1}^{k_{n}}p_{n,i}^{c_{n,i}}$ 取 $\text{lcm}$ 就是 $\prod_{i=1}^{\max\{k\}}p_{i}^{\max\{c\}}$(在一个数中存在的 $p$ 且在另一个中不存在的置为 $1$),对于这题即 $2^{\max\{a\}}\cdot 3^{\max\{b\}}$,让两个 $\max$ 分别等于题目给出的 $a,b$。

现在转化一下题意,在 $u,v$ 中选出一条可非简单路径使得 $\max\{a\},\max\{b\}$ 分别等于给出的 $a,b$。

有一个 naive 的想法是缩点后 LCT 维护结果发现不太行。(好像是我不行)

既然想到了用 LCT 维护连通性那么同样可以想到用 DSU 来维护,把所有满足条件的边放入一个 DSU 然后看 $u,v$ 是否能被这些边联通。

有两个关键字欸,我们离线询问排序吧。

把所有边按照 $a$ 排序,把询问按 $b$ 排序。

然后对边的标号分块,这样块内边 $a$ 有序。

然后把每个询问放在满足前面块的 $a$ 都小于等于这个询问的 $a$ 的块里,并且把块内询问 $b$ 排序,然后就可以做了。

代码狼 BH 今天又口胡题解没代码了。

Oops, something went wrong.

Description

Link.

给出一个堆,然后让你填数进去,使得其满足小根堆的性质,并使编号靠前的点的数最大。

Solution

考虑贪心,把原数列降序排序,然后因为这个东西是整除分块的形式,所以一个结点的子树一定对应的是原序列的一个子区间。不过这个东西并不能用根号分治来做。

然后对于一个子树的根 $u$,我们给他 $[l,r]$ 这个区间,$\text{subtree}(u)-\{u\}=a_{[l,r)}$,结点 $u=a_{r}$,$[l,r]$ 按需分配,反正就优先队列维护就行了。

代码大概长成这样子:

void Search(int x) {
  for(int y in SonOf(x)) Search(y);
  if(x != VirtualRoot) {
    ans[x] = PriorityQueue.top();
    PriorityQueue.pop();
  }
}

那个 VirtualRoot 是为了编码方便弄出来的一个虚根,不用管。同时发现这个做法只有在 $\forall i,j,s.t.d_{i}\neq d_{j}$ 的时候才是对的。Hack 数据网上找找应该有。

对于一个与 $u$ 同层的结点,可能会出现这个结点与 $u$ 的儿子交换点权后更优的情况。

对值域 $[1,n]$ 建出一棵线段树,同时定义 $pos_{i}$ 为 $\max\{j\mid a_{i}=a_{i+1}=\cdots=a_{j}\}$。然后每次查找能用的个数不小于该结点子树大小的位置,有多解跑到最右边,然后把这个位置的能用个数减去子树大小。描述的不清楚,建议做代码阅读理解领略一下精神。

#include<bits/stdc++.h>
struct node
{
    int mn,tag;
    node(int A=0,int B=0)
    {
        mn=A;
        tag=B;
    }
}nodes[2000010];
std::vector<std::vector<int> > e;
int n,siz[500010],ans[500010];
double k;
void dfs(int x)
{
    siz[x]=1;
    for(int i=0;i<int(e[x].size());++i)
    {
        int y=e[x][i];
        dfs(y);
        siz[x]+=siz[y];
    }
}
void build(int l,int r,int x)
{
    if(l^r)
    {
        int mid=(l+r)>>1;
        build(l,mid,x<<1);
        build(mid+1,r,x<<1|1);
        nodes[x].mn=std::min(nodes[x<<1].mn,nodes[x<<1|1].mn);
    }
    else    nodes[x].mn=l;
}
void push_down(int x)
{
    if(nodes[x].tag)
    {
        int &cur=nodes[x].tag;
        nodes[x<<1].mn+=cur;
        nodes[x<<1|1].mn+=cur;
        nodes[x<<1].tag+=cur;
        nodes[x<<1|1].tag+=cur;
        cur=0;
    }
}
void ins(int l,int r,int x,int fr,int ba,int delta)
{
    if(l>ba || r<fr)    return;
    if(l>=fr && r<=ba)
    {
        nodes[x].mn+=delta;
        nodes[x].tag+=delta;
    }
    else
    {
        int mid=(l+r)>>1;
        push_down(x);
        ins(l,mid,x<<1,fr,ba,delta);
        ins(mid+1,r,x<<1|1,fr,ba,delta);
        nodes[x].mn=std::min(nodes[x<<1].mn,nodes[x<<1|1].mn);
    }
}
int find(int l,int r,int x,int d)
{
    if(l^r)
    {
        int mid=(l+r)>>1;
        push_down(x);
        if(d<=nodes[x<<1|1].mn)    return find(l,mid,x<<1,d);
        else    return find(mid+1,r,x<<1|1,d);
    }
    else    return l+(nodes[x].mn<d);
}
int getDiv(int x,double k)
{
    return int(std::floor(double(x)/k));
}
int main()
{
    scanf("%d %lf",&n,&k);
    e.resize(n+1);
    std::vector<int> a(n+10),pos(n+10);
    for(int i=1;i<=n;++i)    scanf("%d",&a[i]);
    std::sort(a.begin()+1,a.begin()+n+1,std::greater<int>());
    for(int i=n-1;i;--i)
    {
        if(a[i]==a[i+1])    pos[i]=pos[i+1]+1;
    }
    for(int i=1;i<=n;++i)    e[getDiv(i,k)].emplace_back(i);
    dfs(0);
    build(1,n,1);
    for(int i=1;i<=n;++i)
    {
        if(getDiv(i,k)^getDiv(i-1,k))    ins(1,n,1,ans[getDiv(i,k)],n,siz[getDiv(i,k)]-1);
        int tmp=find(1,n,1,siz[i]);
        tmp+=pos[tmp];
        ++pos[tmp];
        tmp-=pos[tmp]-1;
        ans[i]=tmp;
        ins(1,n,1,tmp,n,-siz[i]);
    }
    for(int i=1;i<=n;++i)    printf("%d ",a[ans[i]]);
    return 0;
}

Description

Link.

Given is a rooted tree with the $\sf1$-th node as the root.

The tree will be given in this way: it will tell you that the parent of the $\sf i$-th node is $a_{i}$.

Supporting the following operations:

  • 1 l r x: let $\sf \forall i\in[l,r],a_{i}=max\{a_{i}-x,1\}$.
  • 2 u v: find the LCA (Lowest Common Ancestor) of $\sf u$ and $\sf v$.

Solution

考虑到修改操作是对结点进行的操作,然后这个东西不太能直接 LCT 或树剖,考虑照序列来分块,那么我们来对结点编号分块。

  1. 修改;

$\quad$维护一个属性 $\sf top_{u}$ 表示在原树上结点 $\sf u$ 的祖先中不和 $\sf u$ 在同一个块里面的编号最大的一个结点的编号,如果不存在的话就令 $\sf top_{u}=1$。这样的话你从结点 $\sf u$ 跳到 root 的复杂度为 $\sf O(\sqrt{n})$。接下来考虑怎么维护这个东西。

$\quad$散块我们直接暴力扫着改;对于整块,可以发现如果一个块的被修改次数超过了块的大小,那么就一定会有 $\sf top_{u}=fa_{u}$。

  1. 询问。

$\quad$分三个类讨论,这个比较好做(差不多和树剖找 LCA 一个样子)。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,m,top[400010],deln[1000],tag[1000],belong[400010],bl[1000],br[1000],fa[400010],bs;
#define gtlf(x) ((x-1)*bs+1)
#define gtrg(x) (min(x*bs,n))
void updtop(LL x)
{
    if(belong[x]^belong[fa[x]])    top[x]=fa[x];
    else    top[x]=top[fa[x]];
}
void turndown(LL x)
{
    if(tag[x])
    {
        for(LL i=gtlf(x);i<=gtrg(x);++i)    fa[i]=max(fa[i]-tag[x],1ll);
        tag[x]=0;
    }
}
template<typename T>
void read(T &hhh)
{
    T x=0;
    char c=getchar();
    while(c<'0' || c>'9')    c=getchar();
    while(c>='0' && c<='9')    x=(x<<3)+(x<<1)+(c&15),c=getchar();
    hhh=x;
}
template<typename T>
void write(T x,char las='\n')
{
    static int st[100],top=0;
    do st[++top]=x%10,x/=10; while(x);
    while(top)    putchar(st[top]^'0'),--top;
    putchar(las);
}
int main()
{
    read(n),read(m),bs=sqrt(double(n))+1,fa[1]=belong[1]=1;
    for(LL i=2;i<=n;++i)    read(fa[i]);
    for(LL i=2;i<=n;++i)    belong[i]=(i-1)/bs+1,updtop(i);
    LL las=0;
    while(m--)
    {
        LL opt; read(opt);
        if(opt==1)
        {
            LL opl,opr,opx;
            read(opl),read(opr),read(opx);
            opl^=las,opr^=las,opx^=las;
            turndown(belong[opl]);
            if(belong[opl]==belong[opr])
            {
                turndown(belong[opl]);
                for(LL i=opl;i<=opr;++i)    fa[i]=max(fa[i]-opx,1ll),updtop(i);
                for(LL i=opr+1;i<=gtrg(belong[opl]);++i)    updtop(i);
            }
            else
            {
                turndown(belong[opl]);
                for(LL i=opl;i<=gtrg(belong[opl]);++i)    fa[i]=max(fa[i]-opx,1ll),updtop(i);
                for(LL i=gtlf(belong[opl]);i<opl;++i)    updtop(i);
                turndown(belong[opr]);
                for(LL i=gtlf(belong[opr]);i<=opr;++i)    fa[i]=max(fa[i]-opx,1ll),updtop(i);
                for(LL i=opr+1;i<=gtrg(belong[opr]);++i)    updtop(i);
                for(LL i=belong[opl]+1;i<belong[opr];++i)
                {
                    if(deln[i]>=bs)    tag[i]+=opx;
                    else
                    {
                        ++deln[i];
                        for(LL j=gtlf(i);j<=gtrg(i);++j)    fa[j]=max(fa[j]-opx,1ll),updtop(j);
                    }
                }
            }
        }
        else
        {
            LL opx,opy; read(opx),read(opy);
            opx^=las,opy^=las;
            while(opx^opy)
            {
                LL fopx,fopy;
                if(deln[belong[opx]] < bs) fopx=top[opx];
                else fopx = max(1LL , fa[opx] - tag[belong[opx]]);
                if(deln[belong[opy]] < bs) fopy=top[opy];
                else fopy = max(1LL , fa[opy] - tag[belong[opy]]);
                if(belong[opx]^belong[opy])
                {
                    if(belong[opx]>belong[opy])    opx=fopx;
                    else    opy=fopy;
                }
                else if(fopx^fopy)    opx=fopx,opy=fopy;
                else
                {
                    if(opx>opy)    turndown(belong[opx]),opx=max(1LL , fa[opx] - tag[belong[opx]]);
                    else    turndown(belong[opy]),opy=max(1LL , fa[opy] - tag[belong[opy]]);
                }
            }
            write(las=opx);
        }
    }
    return 0;
}

Description

Link.

  • 区间推平;
  • 区间数颜色。

Solution

考虑无修的情况,我们是采用维护每个数的 $pre$ 来做的。具体一点就是对于每一个 $a_{i}$ 维护 $pre_{i}=\max\{j\in[1,i),s.t.a_{j}=a_{i}\}$。然后数区间内 $pre$ 严格小于左端点的元素个数。

区间推平不好做,考虑削弱这一层修改,考虑单点修改怎么做。

修改一个 $a_{i}=x$,则受影响的下标有:

  • $j,s.t.pre_{j}=i$;
  • $i$;
  • $\min\{j\in(i,n],s.t.a_{j}=x\}$。

这个东西套个 std::map 能直接维护。

对于区间修改,有这样的结论:

对于所有修改,$pre$ 变化次数为 $\mathcal{O}(n+m)$。

做不来区间推平翻题解翻来的,至于证明不太会,摊还分析证复杂度没用过。

反正综上就能做了嘛,不知道为什么那么多人都喜欢写树套树,反正我是 CDQ。

//in the autumn sky
#include<bits/stdc++.h>
using namespace std;
#define fs first
#define sc second
struct oper
{
    int opt,opl,opr,opx;
    oper(int A=0,int B=0,int C=0,int D=0){opt=A,opl=B,opr=C,opx=D;}
};
struct ST_modify
{
    int ID,pos,pr,val;
    ST_modify(int A=0,int B=0,int C=0,int D=0){ID=A,pos=B,pr=C,val=D;}
    bool operator<(const ST_modify &ano)const{return pr<ano.pr;}
};
struct ST_query
{
    int ID,l,r;
    ST_query(int A=0,int B=0,int C=0){ID=A,l=B,r=C;}
    bool operator<(const ST_query &ano)const{return l<ano.l;}
};
vector<int> pri;
vector<ST_modify> mod;
vector<ST_query> que;
int pre[100010],n,m,tr[100010],curInd,ans[100010];
map<int,int> mapo[200010];
map<int,pair<int,int> > exmapo; // for ODT
#define lowbit(x) ((x)&(-(x)))
void ins(int pos,int delta)
{
    ++pos;
    while(pos<=n)
    {
        tr[pos-1]+=delta;
        pos+=lowbit(pos);
    }
}
int find(int pos)
{
    int res=0;
    while(pos)
    {
        res+=tr[pos-1];
        pos^=lowbit(pos);
    }
    return res;
}
void ODT_split(int pos)
{
    auto iter=exmapo.lower_bound(pos);
    int stan=n;
    if(iter!=exmapo.end())  stan=iter->fs;
    if(stan^pos)
    {
        --iter;
        exmapo.emplace(pos,iter->sc);
        auto exiter=mapo[iter->sc.sc].emplace(pos,iter->sc.fs).fs;
        iter->sc.fs=pos;
        prev(exiter)->sc=pos;
    }
}
void pushOp(int pos,int after,int ID)
{
    mod.emplace_back(ST_modify(ID,pos,pre[pos],-1));
    pre[pos]=after;
    mod.emplace_back(ST_modify(ID,pos,after,1));
}
void rawGrass(int onel,int oner,int anol,int anor,int parl,int parr)
{
    if(onel==oner || anol==anor)
    {
        sort(mod.begin()+onel,mod.begin()+oner);
        sort(que.begin()+anol,que.begin()+anor);
    }
    else
    {
        int mid=(parl+parr)>>1,exmid1=onel,exmid2=anol;
        while(exmid1<oner && mod[exmid1].ID<mid)    ++exmid1;
        while(exmid2<anor && que[exmid2].ID<mid)    ++exmid2;
        rawGrass(onel,exmid1,anol,exmid2,parl,mid);
        rawGrass(exmid1,oner,exmid2,anor,mid,parr);
        int ex=onel;
        for(int i=exmid2;i<anor;++i)
        {
            while(ex<exmid1 && mod[ex].pr<que[i].l)    ins(mod[ex].pos,mod[ex].val),++ex;
            ans[que[i].ID]+=find(que[i].r)-find(que[i].l);
        }
        for(int i=onel;i<ex;++i)    ins(mod[i].pos,-mod[i].val);
        inplace_merge(mod.begin()+onel,mod.begin()+exmid1,mod.begin()+oner);
        inplace_merge(que.begin()+anol,que.begin()+exmid2,que.begin()+anor);
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    vector<int> a(n);
    vector<oper> b(m);
    for(int &i:a)   scanf("%d",&i),pri.emplace_back(i);
    for(oper &i:b)
    {
        scanf("%d %d %d",&i.opt,&i.opl,&i.opr);
        if(i.opt==1)    scanf("%d",&i.opx),pri.emplace_back(i.opx);
        --i.opl;
    }
    sort(pri.begin(),pri.end());
    pri.erase(unique(pri.begin(),pri.end()),pri.end());
    for(int &i:a)   i=lower_bound(pri.begin(),pri.end(),i)-pri.begin();
    for(oper &i:b)
    {
        if(i.opt==1)    i.opx=lower_bound(pri.begin(),pri.end(),i.opx)-pri.begin();
    }
    curInd=0;
    for(int i:a)
    {
        if(mapo[i].size()!=int(0))  pre[curInd]=prev(mapo[i].end())->fs;
        else    pre[curInd]=-1;
        mod.emplace_back(ST_modify(-1,curInd,pre[curInd],1));
        mapo[i].emplace(curInd,curInd+1);
        exmapo.emplace(curInd,make_pair(curInd+1,i));
        ++curInd;
    }
    curInd=0;
    for(oper i:b)
    {
        int ty=i.opt,l=i.opl,r=i.opr,x=i.opx;
        if(ty==1)
        {
            ODT_split(l),ODT_split(r);
            auto iter=mapo[x].lower_bound(l);
            auto ptr=exmapo.lower_bound(l);
            int rpe=(iter!=mapo[x].begin())?((iter=prev(iter))->sc-1):(-1); // for Suf
            while(ptr!=exmapo.end())
            {
                if(ptr->fs==r)  break;
                pushOp(ptr->fs,rpe,curInd);
                int tmp=ptr->sc.sc;
                auto exiter=mapo[tmp].erase(mapo[tmp].find(ptr->fs));
                if(exiter!=mapo[tmp].end())
                {
                    if(exiter==mapo[tmp].begin())   pushOp(exiter->fs,-1,curInd);
                    else    pushOp(exiter->fs,prev(exiter)->sc-1,curInd);
                }
                rpe=ptr->sc.fs-1;
                ptr=exmapo.erase(ptr);
            }
            iter=mapo[x].lower_bound(r);
            if(iter!=mapo[x].end()) pushOp(iter->fs,r-1,curInd);
            exmapo.emplace(l,make_pair(r,x));
            mapo[x].emplace(l,r);
        }
        else    que.emplace_back(ST_query(curInd,l,r));
        ++curInd;
    }
    rawGrass(0,int(mod.size()),0,int(que.size()),-1,m);
    curInd=0;
    for(oper i:b)
    {
        if(i.opt==2)    printf("%d\n",ans[curInd]);
        ++curInd;
    }
    return 0;
}

所以 Chinese Round 出 DS 是传统了对吧。

Description

Link.

Given is a rooted tree with the $\sf1$-th node as the root.

The tree will be given in this way: it will tell you that the parent of the $\sf i$-th node is $a_{i}$.

Supporting the following operations:

  • 1 l r x: let $\sf \forall i\in[l,r],a_{i}=max\{a_{i}-x,1\}$.
  • 2 u v: find the LCA (Lowest Common Ancestor) of $\sf u$ and $\sf v$.

Solution

经典永流传。

考虑到修改操作是对结点进行的操作,然后这个东西不太能直接 LCT 或树剖,考虑照序列来分块,那么我们来对结点编号分块。

  1. 修改;

$\quad$维护一个属性 $\sf top_{u}$ 表示在原树上结点 $\sf u$ 的祖先中不和 $\sf u$ 在同一个块里面的编号最大的一个结点的编号,如果不存在的话就令 $\sf top_{u}=1$。这样的话你从结点 $\sf u$ 跳到 root 的复杂度为 $\sf O(\sqrt{n})$。接下来考虑怎么维护这个东西。

$\quad$散块我们直接暴力扫着改;对于整块,可以发现如果一个块的被修改次数超过了块的大小,那么就一定会有 $\sf top_{u}=fa_{u}$。

  1. 询问。

$\quad$分三个类讨论,这个比较好做(差不多和树剖找 LCA 一个样子)。

#include<bits/stdc++.h>
using namespace std;
int n,m,top[100010],deln[320],tag[320],belong[100010],bl[320],br[320],fa[100010],bs;
#define gtlf(x) ((x-1)*bs+1)
#define gtrg(x) (min(x*bs,n))
void updtop(int x)
{
    if(belong[x]^belong[fa[x]])    top[x]=fa[x];
    else    top[x]=top[fa[x]];
}
void turndown(int x)
{
    if(tag[x])
    {
        for(int i=gtlf(x);i<=gtrg(x);++i)    fa[i]=max(fa[i]-tag[x],1);
        tag[x]=0;
    }
}
int main()
{
    scanf("%d %d",&n,&m),bs=sqrt(double(n))+1,fa[1]=belong[1]=1;
    for(int i=2;i<=n;++i)    scanf("%d",&fa[i]);
    for(int i=2;i<=n;++i)    belong[i]=(i-1)/bs+1,updtop(i);
    while(m--)
    {
        int opt; scanf("%d",&opt);
        if(opt==1)
        {
            int opl,opr,opx;
            scanf("%d %d %d",&opl,&opr,&opx);
            turndown(belong[opl]);
            if(belong[opl]==belong[opr])
            {
                turndown(belong[opl]);
                for(int i=opl;i<=opr;++i)    fa[i]=max(fa[i]-opx,1),updtop(i);
                for(int i=opr+1;i<=gtrg(belong[opl]);++i)    updtop(i);
            }
            else
            {
                turndown(belong[opl]);
                for(int i=opl;i<=gtrg(belong[opl]);++i)    fa[i]=max(fa[i]-opx,1),updtop(i);
                for(int i=gtlf(belong[opl]);i<opl;++i)    updtop(i);
                turndown(belong[opr]);
                for(int i=gtlf(belong[opr]);i<=opr;++i)    fa[i]=max(fa[i]-opx,1),updtop(i);
                for(int i=opr+1;i<=gtrg(belong[opr]);++i)    updtop(i);
                for(int i=belong[opl]+1;i<belong[opr];++i)
                {
                    if(deln[i]>=bs)    tag[i]+=opx;
                    else
                    {
                        ++deln[i];
                        for(int j=gtlf(i);j<=gtrg(i);++j)    fa[j]=max(fa[j]-opx,1),updtop(j);
                    }
                }
            }
        }
        else
        {
            int opx,opy; scanf("%d %d",&opx,&opy);
            while(opx^opy)
            {
                int fopx,fopy;
                if(deln[belong[opx]]>=bs)    turndown(belong[opx]),fopx=fa[opx];
                else    fopx=top[opx];
                if(deln[belong[opy]]>=bs)    turndown(belong[opy]),fopy=fa[opy];
                else    fopy=top[opy];
                if(belong[opx]^belong[opy])
                {
                    if(belong[opx]>belong[opy])    opx=fopx;
                    else    opy=fopy;
                }
                else if(fopx^fopy)    opx=fopx,opy=fopy;
                else
                {
                    if(opx>opy)    turndown(belong[opx]),opx=fa[opx];
                    else    turndown(belong[opy]),opy=fa[opy];
                }
            }
            printf("%d\n",opx);
        }
    }
    return 0;
}