其他分享
首页 > 其他分享> > 【题解】「Ynoi2018」天降之物 [*hard]

【题解】「Ynoi2018」天降之物 [*hard]

作者:互联网

最开始的想法是对序列分块,然后每个块维护一个 \(\sqrt{n}\times \sqrt{n}\) 的矩阵表示这个块中颜色与颜色的距离,再维护每个颜色到左右端点的最短距离。

容易发现查询的时候很好查询,问题在于修改操作:要对每一个块进行处理,光是枚举块就要用掉 \(O(\sqrt{n})\) 的时间,那就只能在一个块上留下 \(O(1)\) 的时间来修改了,这似乎很难办。

深入思考一下,假设一个块中最开始有 \(m\) 个颜色,那么修改 \(x\) 为 \(y\) :

那么一个块最多产生多少次操作?因为 \(m\) 最大为 \(\sqrt{n}\) ,所以一个块中的操作次数其实是 \(O(n)\) 级别的。所以总操作次数就是 \(O(n\sqrt{n})\) 。

这个做法再拓展拓展大概就能做"MtOI2019 手牵手走向明天"。

关于拓展:

这个时候大概就做完了,因为我没有实现,所以不放代码。

在这个做法中我最没有信心的就是"区间修改"部分,常数似乎不小 ...

代码咕咕咕。


事实上,在第四分块中,我们完全可以避免这种复杂,卡常,码农的做法。

回忆一下之前的做法,对于每一个块中的每一种颜色维护了一个与同一块中其他颜色的最小距离。如果将这个做法换成全局的话,则需要 \(O(n^2)\) 的空间。

同时可以发现,对于查询的话,复杂度跟 \(x,y\) 的出现次数密切相关。

那么,能不能对出现次数超过 \(\sqrt{n}\) 的不超过 \(\sqrt{n}\) 个颜色,每一个颜色维护一个与其他颜色的最小距离呢?这样子的话空间花费就是 \(O(n\sqrt{n})\) 的了。

这样的话查询时,如果 \(x,y\) 有任意一方的出现次数超过 \(\sqrt{n}\),就直接查表,否则用 \(O(\sqrt{n})\) 的时间暴力求解。

接下来考虑合并颜色:

然后就做完了。


关于实现:

const int N=1e5+5;
const int SqrtN=1e2+5;

const int w=SqrtN-1;
int n,m,sqrtn,last_ans,a[N];

// {{{ Data_Struct_Block

bool live[N];
int rt[N],fa[N],col[N];
int top,tmp_las,id[N],pot[N],idsta[SqrtN],dis[SqrtN][N];
std::vector <int> sta[N],tmp_sort;

int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);}

inline void update_dis(int &_id,const int &i) {
    if(!_id) _id=idsta[top--],memset(dis[_id],64,sizeof(dis[_id]));
    tmp_las=-inf;
    lep(j,1,n) {if(col[find(j)]==i) tmp_las=j; chkmin(dis[_id][pot[col[find(j)]]],j-tmp_las);}
    tmp_las=inf;
    rep(j,n,1) {if(col[find(j)]==i) tmp_las=j; chkmin(dis[_id][pot[col[find(j)]]],tmp_las-j);}
}

inline void merge_sort(std::vector <int> &sx,std::vector <int> &sy) {
    int ix=0,iy=0; const int limx=sx.size()-1,limy=sy.size()-1;
    while(ix<=limx&&iy<=limy) tmp_sort.pb(sx[ix]>sy[iy]?sy[iy++]:sx[ix++]);
    while(ix<=limx) tmp_sort.pb(sx[ix++]);
    while(iy<=limy) tmp_sort.pb(sy[iy++]);
    return sy=tmp_sort,tmp_sort.clear(),sx.clear();
}
inline void merge(int x,int y) {
    if(x==y||!live[x]) return ;
    if(!live[y]&&live[x])
        return swap(live[x],live[y]),swap(pot[x],pot[y]),col[rt[y]=rt[x]]=y,rt[x]=0,void();
    if(!id[pot[y]]) swap(pot[x],pot[y]);

    const int &px=pot[x],&py=pot[y];
    live[x]=0,fa[rt[x]]=rt[y],col[rt[x]]=0,rt[x]=0;

    if(id[py]&&id[px]) {
        lep(i,1,n) chkmin(dis[id[py]][i],dis[id[px]][i]);
        idsta[++top]=id[px],id[px]=0;
    }
    merge_sort(sta[px],sta[py]);
    if(sta[py].size()>sqrtn) update_dis(id[py],y),sta[py].clear();
    lep(i,1,w) chkmin(dis[i][py],dis[i][px]),dis[i][px]=inf;
}

inline int calc(const std::vector <int> &sx,const std::vector <int> &sy) {
    int ans=inf,ix=0,iy=0;
    const int limx=sx.size()-1,limy=sy.size()-1;
    while(ix<=limx&&iy<=limy) (sx[ix]<sy[iy])?chkmin(ans,sy[iy]-sx[ix++]):chkmin(ans,sx[ix]-sy[iy++]);
    return ans;
}
inline void query(int x,int y) {
    if(!live[x]||!live[y]) return last_ans=0,puts("Ikaros"),void();
    if(x==y) return printf("%d\n",last_ans=0),void();
    x=pot[x],y=pot[y];
    int res=calc(sta[x],sta[y]);
    printf("%d\n",last_ans=min(res,min(dis[id[x]][y],dis[id[y]][x])));
}

// }}}

int op,x,y;
int main() {
    IN(n,m),sqrtn=1000;
    lep(i,1,SqrtN-1) idsta[++top]=i;
    memset(dis[0],64,sizeof(dis[0]));

    lep(i,1,n) live[i]=false,pot[i]=i;
    lep(i,1,n) IN(a[i]),live[a[i]]=true,sta[a[i]].pb(i);
    lep(i,1,n) {if(!rt[a[i]]) col[rt[a[i]]=i]=a[i]; fa[i]=rt[a[i]];}
    lep(i,1,n) if(sta[i].size()>sqrtn) update_dis(id[i],i),sta[i].clear();

    while(m--) {
        IN(op,x,y),x^=last_ans,y^=last_ans;
        if(op==1) merge(x,y);
        if(op==2) query(x,y);
    }    
    return 0;
}

标签:Ynoi2018,颜色,int,题解,之物,sqrt,次数,id,dis
来源: https://www.cnblogs.com/losermoonlights/p/14225221.html