Codeforces Round #467 (Div. 1) E:Iqea(点分治)

传送门

题解:
把每列的不同联通块看做一个点,相邻列若有共同的纵坐标有格子那么连边,这样会形成一棵树,且网格上两点的路径就是树上的一条路径。

然后就是点分治裸题,不过算距离比较麻烦。
注意到两点的路径最终在某一列的某一块停止,且进入的时候一定之前走的是最短距离(容易证明进入的点也是唯一的),那么这两点的距离等于两个最短距离加上进入的纵坐标绝对值差,即: da+db+|yayb| d a + d b + | y a − y b |

后面的绝对值是前缀最小值,维护两个树状数组即可。时间复杂度 O(nlog2n) O ( n log 2 ⁡ n )

#include <bits/stdc++.h>
using namespace std;
typedef vector <int> Vec;
typedef set <int> Set;
typedef map <int,int> Map;
inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}
const int N=3e5+50,INF=0x3f3f3f3f;
int n,oc,tc;
Vec coors[N];
Vec oe[N];
Vec te[N];
Vec bit[N][2];
Map id[N];
int bl[N],ed[N],xd[N],bg[N],mx,total,G;
int yc[N][30],dis[N][30],sze[N],d[N],fa[N],vis[N];
inline void dfs(int x,int f) {
    sze[x]=ed[x];
    for(int e=te[x].size()-1;e>=0;e--) {
        int v=te[x][e]; if(v==f||vis[v]) continue;
        dfs(v,x); sze[x]+=sze[v];
    }
}
inline void calcG(int x,int f) {
    int mxv=total-sze[x];
    for(int e=te[x].size()-1;e>=0;e--) {
        int v=te[x][e]; if(v==f||vis[v]) continue;
        calcG(v,x); mxv=max(mxv,sze[v]);
    }
    if(mxv<mx) G=x,mx=mxv;
}
queue <int> q;
inline void solve(int x,int f) {
    fa[x]=f; d[x]=d[f]+1; vis[x]=1;
    for(int i=1,o=id[xd[x]][coors[xd[x]][bg[x]]];i<=ed[x];++i,++o) {dis[o][d[x]]=0; yc[o][d[x]]=i; q.push(o);}
    while(!q.empty()) {
        int u=q.front(); q.pop();
        for(int e=oe[u].size()-1;e>=0;e--) {
            int v=oe[u][e]; if(vis[bl[v]] || yc[v][d[x]]) continue;
            dis[v][d[x]]=dis[u][d[x]]+1;
            yc[v][d[x]]=yc[u][d[x]];
            q.push(v);
        }
    }
    for(int i=0;i<=ed[x];++i) bit[x][0].push_back(INF), bit[x][1].push_back(INF); 
    for(int e=te[x].size()-1;e>=0;e--) {
        int v=te[x][e]; if(vis[v]) continue;
        dfs(v,x); mx=total=sze[v];
        calcG(v,x); solve(G,x);
    }
}
inline void inc(Vec &a,int p,int v) {for(int i=p;i<a.size();i+=(i&(-i))) a[i]=min(a[i],v);}
inline int ask(Vec &a,int p,int v=INF) {
    for(int i=p;i;i-=(i&(-i))) v=min(v,a[i]);
    return v;
}
inline void upt(int o) {
    for(int j=bl[o];j;j=fa[j]) {
        inc(bit[j][0],yc[o][d[j]],dis[o][d[j]]-yc[o][d[j]]);
        inc(bit[j][1],ed[j]-yc[o][d[j]]+1,dis[o][d[j]]+yc[o][d[j]]);
    }
}
inline int ask(int o) {
    int ans=INF;
    for(int j=bl[o];j;j=fa[j]) {
        ans=min(ans,ask(bit[j][0],yc[o][d[j]])+dis[o][d[j]]+yc[o][d[j]]);
        ans=min(ans,ask(bit[j][1],ed[j]-yc[o][d[j]]+1)+dis[o][d[j]]-yc[o][d[j]]);
    } 
    return ans;
}
int main() {
    n=rd();
    for(int i=1;i<=n;i++) {
        int x=rd(), y=rd();
        coors[x].push_back(y);
    }
    for(int i=1;i<N;i++) {
        if(!coors[i].size()) continue;
        sort(coors[i].begin(),coors[i].end());
        coors[i].erase(unique(coors[i].begin(),coors[i].end()),coors[i].end());
        for(int p1=0,p2=0;p2<coors[i].size();++p2) {
            int y=coors[i][p2]; id[i][y]=++oc;
            if(p2&&coors[i][p2-1]+1==y) {oe[oc].push_back(oc-1); oe[oc-1].push_back(oc);}
            else ++tc,bg[tc]=p2,xd[tc]=i;
            bl[oc]=tc; ++ed[tc];
            while(p1!=coors[i-1].size()&&coors[i-1][p1]<y) ++p1;
            if(p1!=coors[i-1].size()&&coors[i-1][p1]==y) {
                int o=id[i-1][y];
                oe[o].push_back(oc); oe[oc].push_back(o);
                te[bl[o]].push_back(tc); te[tc].push_back(bl[o]);
            }
        }
    }
    for(int i=1;i<=tc;i++) {
        sort(te[i].begin(),te[i].end());
        te[i].erase(unique(te[i].begin(),te[i].end()),te[i].end());
    }
    dfs(1,0); mx=total=sze[1];
    calcG(1,0); solve(G,0);
    for(int i=rd();i;i--) {
        int op=rd(), x=rd(), y=rd(), o=id[x][y];
        if(op==1) {
            upt(o);
        } else {
            int t=ask(o);
            printf("%d\n",(t<=n)?t:-1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值