码迷,mamicode.com
首页 > 其他好文 > 详细

[bzoj1905] [ZJOI2007] Hide 捉迷藏

时间:2020-02-27 19:16:32      阅读:42      评论:0      收藏:0      [点我收藏+]

标签:ace   处理   git   amp   还需   line   cst   log   需要   

题意简述

给定一棵 \(n\) 个点的树,起初每个点都为黑色。
2种操作,要么改变某个点的颜色(由黑至白或由白至黑),要么询问距离最远的两个黑点间的距离。
\(m\) 次操作。

\(n\leq 10^5,m\leq 2\times 10^5\)


想法

动态点分治模板题。

如果只有一次询问操作,那么显然可以用点分治来做。(树形 \(dp\) 也可以,但那样不容易拓展到动态的情况)
点分治时,以每个点为根时,统计过它的满足条件的路径即可。
我们需要知道的只是以该点 \(u\) 的每个子节点 \(v\) 为根的子树到该点的最长距离 \(mx[v]\)
过该点的路径的最长距离为 所有 \(mx[v]\) 中的最大值+次大值。注意如果该点为黑点,最长距离则为 所有 \(mx[v]\)\(0\) 中的最大值+次大值。
同时我们应统计出以 \(u\) 为根的子树中所有点到上一个分治中心的距离的最大值 \(mx[u]\)

回到这道题,有修改和多次查询。
于是建立 “点分树”,即将当前分治中心与它的各子节点的分治中心连边形成的树。
这棵树有一些性质:
1.只有 \(logn\)
2.原树中以每个分治中心为根的子树里的所有点 就是 点分树中以它为根的子树中的所有点。
3.修改一个点 \(x\) ,影响到的是点分树上所有 \(x\) 的祖先为分治中心的情况。
设“点分树”中点 \(u\) 的父节点为 \(pa[u]\)

在此题中,对于每个点 \(u\),维护堆 \(c[u]\) 记录 \(u\) 为分治中心的子树中的所有黑点到 \(pa[u]\) 的距离,堆 \(b[u]\) 记录 \(u\) 为分治中心时各子节点到 \(u\) 的最大距离。即 \(b[u]\) 中的值,是所有 $c[v].top(),如果 \(u\) 为黑点则还需加上0。
对全局维护堆 \(a\) 记录过每个分治中心的最长距离。\(a\) 中的值,就是 \(b[u]\) 中的最大值+次大值。

由于有修改,所以堆需要满足可删除。
用两个优先队列维护一个堆即可。

还有一个问题,如何快速求原树上两点间距离?
我们知道倍增和树剖都是 \(O(logn)\) 的,但更快的方法是 \(st\) 表+ \(dfs\) 序。
这个 \(dfs\) 序很特殊,每次访问完 \(u\) 的子节点 \(v\) 后,要在序列中再加入 \(u\) 。记录进入每个点的时间 \(dfn[u]\)
在这个序列上用 \(O(nlogn)\) 预处理出 \(st\) 表,之后查询 \(x\)\(y\)\(lca\) 就是 \(dfn[x]\)\(dfn[y]\) 着一段序列中深度最小的点,\(O(1)\) 可求。


总结

技巧

1.\(O(1)\) 求静态树上两点的 \(lca\)\(st\) 表+ \(dfs\)

int tot,dfn[N],num[N*2],dep[N];
void dfs(int u){
    int v;
    dfn[u]=++tot; num[tot]=u;
    for(node *p=h1[u];p;p=p->nxt)
        if(!dfn[v=p->v]) {
            dep[v]=dep[u]+1;
            dfs(v);
            num[++tot]=u; //与普通dfs序不同的地方!
        }
}
int st[N*2][18],lg[N*2];
void getst(){
    dep[1]=1; dfs(1);
    for(int i=1;i<=tot;i++) st[i][0]=dep[num[i]];
    for(int j=1;j<18;j++){
        int t=(1<<j);
        for(int i=1;i+t-1<=tot;i++)
            st[i][j]=min(st[i][j-1],st[i+t/2][j-1]);/**/
    }
    int t=0,cur=1;
    for(int i=1;i<=tot;i++)
        if(i<cur) lg[i]=t-1;
        else {
            lg[i]=t;
            t++; cur*=2;
        }
}
int lca(int x,int y){ //lca的深度
    x=dfn[x]; y=dfn[y];
    if(x>y) swap(x,y);
    int t=lg[y-x+1];
    return min(st[x][t],st[y+1-(1<<t)][t]);
}

2.可删堆
用两个优先队列维护,一个维护所有的,一个维护删除的。

struct heap{
    priority_queue<int> q,d;
    void ins(int x) { q.push(x); }
    void del(int x) { d.push(x); }
    void pop(){ //删除最大值
        while(d.size() && d.top()==q.top()) q.pop(),d.pop();
        q.pop();
    }
    int fr(){ //堆中最大值
        while(d.size() && d.top()==q.top()) q.pop(),d.pop();
        return q.top();
    }
    int size() { return q.size()-d.size(); }
};

手残

\(st\) 表中注意第二维不要开小了!
注意判断堆是否为空。


代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>

using namespace std;

int read(){
    int x=0;
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x;
}

const int N = 100005;

int n;
struct node{
    int v;
    node *nxt;
}pool[N*4],*h1[N],*h[N];
int cnt;
void addedge1(int u,int v){
    node *p=&pool[++cnt],*q=&pool[++cnt];
    p->v=v;p->nxt=h1[u];h1[u]=p;
    q->v=u;q->nxt=h1[v];h1[v]=q;
}
void addedge(int u,int v){
    node *p=&pool[++cnt],*q=&pool[++cnt];
    p->v=v;p->nxt=h[u];h[u]=p;
    q->v=u;q->nxt=h[v];h[v]=q;
}

//get lca
int tot,dfn[N],num[N*2],dep[N];
void dfs(int u){
    int v;
    dfn[u]=++tot; num[tot]=u;
    for(node *p=h1[u];p;p=p->nxt)
        if(!dfn[v=p->v]) {
            dep[v]=dep[u]+1;
            dfs(v);
            num[++tot]=u; /**/
        }
}
int st[N*2][18],lg[N*2];
void getst(){
    dep[1]=1; dfs(1);
    for(int i=1;i<=tot;i++) st[i][0]=dep[num[i]];
    for(int j=1;j<18;j++){
        int t=(1<<j);
        for(int i=1;i+t-1<=tot;i++)
            st[i][j]=min(st[i][j-1],st[i+t/2][j-1]);/**/
    }
    int t=0,cur=1;
    for(int i=1;i<=tot;i++)
        if(i<cur) lg[i]=t-1;
        else {
            lg[i]=t;
            t++; cur*=2;
        }
}
int lca(int x,int y){
    x=dfn[x]; y=dfn[y];
    if(x>y) swap(x,y);
    int t=lg[y-x+1];
    return min(st[x][t],st[y+1-(1<<t)][t]);
}
int dis(int x,int y) { 
    return x==0||y==0?dep[x+y]:dep[x]+dep[y]-2*lca(x,y); 
}

//heap
struct heap{
    priority_queue<int> q,d;
    void ins(int x) { q.push(x); }
    void del(int x) { d.push(x); }
    void pop(){
        while(d.size() && d.top()==q.top()) q.pop(),d.pop();
        q.pop();
    }
    int fr(){
        while(d.size() && d.top()==q.top()) q.pop(),d.pop();
        return q.top();
    }
    int se(){
        int x=fr(); pop();
        int y=fr(); q.push(x);
        return x+y;
    }
    int size() { return q.size()-d.size(); }
}a,b[N],c[N];

//build dianfen tree
int rt,root,all,sz[N],mx[N],vis[N],pa[N];
void getrt(int u,int fa){
    int v;
    sz[u]=1; mx[u]=0;
    for(node *p=h1[u];p;p=p->nxt)
        if((v=p->v)!=fa && !vis[v]){
            getrt(v,u);
            sz[u]+=sz[v];
            mx[u]=max(mx[u],sz[v]);
        }
    mx[u]=max(mx[u],all-sz[u]);
    if(mx[u]<mx[rt]) rt=u;
}
void getsz(int u,int fa,int id){
    int v;
    sz[u]=1;
    c[id].ins(dis(u,pa[id]));
    for(node *p=h1[u];p;p=p->nxt)
        if((v=p->v)!=fa && !vis[v]){
            getsz(v,u,id);
            sz[u]+=sz[v];
        }
}
void work(int u){
    int v;
    vis[u]=1;
    c[u].ins(dis(u,pa[u]));
    b[u].ins(0);
    for(node *p=h1[u];p;p=p->nxt)
        if(!vis[v=p->v]){
            getsz(v,u,u);
            all=sz[v]; rt=0; getrt(v,u);
            addedge(u,rt);
            pa[rt]=u; v=rt;
            work(rt);
            b[u].ins(c[v].fr());
        }
    if(b[u].size()>1) a.ins(b[u].se());
}

//modify
void turn_off(int u){//1->0
    int x=u;
    if(b[x].size()>1) a.del(b[x].se());
    b[x].ins(0);
    if(b[x].size()>1) a.ins(b[x].se());
    while(x){
        if(pa[x]) { //del fa
            if(b[pa[x]].size()>1) a.del(b[pa[x]].se());
            if(c[x].size()) b[pa[x]].del(c[x].fr()); /**/
        }
        c[x].ins(dis(u,pa[x]));
        if(pa[x]) { //update fa
            b[pa[x]].ins(c[x].fr());
            if(b[pa[x]].size()>1) a.ins(b[pa[x]].se());
        }
        x=pa[x]; /**/
    }
}
void turn_on(int u){//0->1
    int x=u;
    if(b[x].size()>1) a.del(b[x].se());
    b[x].del(0);
    if(b[x].size()>1) a.ins(b[x].se());
    while(x){
        if(pa[x]) { //del fa
            if(b[pa[x]].size()>1) a.del(b[pa[x]].se());
            if(c[x].size()) b[pa[x]].del(c[x].fr()); /**/
        }
        c[x].del(dis(u,pa[x]));
        if(pa[x]) { //update fa
            if(c[x].size()) b[pa[x]].ins(c[x].fr());/**/
            if(b[pa[x]].size()>1) a.ins(b[pa[x]].se());
        }
        x=pa[x];/**/
    }
}

int lon,lit[N];

int main()
{
    int Q,x;
    char ch[2];
    n=read();
    for(int i=1;i<n;i++) addedge1(read(),read());
    
    getst();
    rt=0; mx[rt]=n+1; all=n; getrt(1,0);
    root=rt;
    work(root);
    
    lon=0;
    Q=read();
    while(Q--){
        scanf("%s",ch);
        if(ch[0]=='C'){
            x=read();
            if(lit[x]) lon--,turn_off(x),lit[x]=0; /*lit*/
            else lon++,turn_on(x),lit[x]=1;
        }
        else{
            if(lon==n) printf("-1\n");
            else if(lon==n-1) printf("0\n");
            else printf("%d\n",a.fr());
        }
    }
    
    return 0;
}

[bzoj1905] [ZJOI2007] Hide 捉迷藏

标签:ace   处理   git   amp   还需   line   cst   log   需要   

原文地址:https://www.cnblogs.com/lindalee/p/12373538.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!