【CodeChef】Querying on a Grid(分治,最短路)

【CodeChef】Querying on a Grid(分治,最短路)

题面

Vjudge
CodeChef

题解

考虑分治处理这个问题,每次取一个\(mid\),对于\(mid\)上的三个点构建最短路径树(因为保证了最短路唯一所以是树)。
如果两点之间的最短路径跨越了\(mid\),那么必定有\(dis[u]+dis[v]\)的和就是最短路径长度。
那么我们在分治过程中考虑每一个\(mid\),取其中\(dis[u]+dis[v]\)的最小值,这样子就很容易可以找到最短路径长度。
然后知道了最短路径长度怎么找到最短路径呢?
我们已经把最短路径树给构出来了,那么只需要在必定跨越的\(mid\)的最短路径树上做修改就好了。
如果直接链加再单点询问,这样子是两个\(log\)的,改成单点修改,子树求和就可以做到一个\(log\)了。
这样子分治+对于每个最短路径树计算答案,复杂度是\(O(nlog^2n)\),似乎还有一个\(3\)的常数。
c++11真好用,写着写着就跟zsy代码一样了(大雾

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 300300
#define pi pair<ll,int>
#define fr first
#define sd second
#define mp make_pair
#define pb push_back
inline ll read()
{
    ll x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
vector<pi> E[MAX];
int n,m,Q;
int id[MAX][3],tot;
int lb(int x){return x&(-x);}
struct SPT
{
    ll dis[MAX],c[MAX];int fa[MAX],dfn[MAX],low[MAX],tim;
    bool vis[MAX];
    vector<int> son[MAX];
    priority_queue<pi,vector<pi>,greater<pi> >Q;
    void dfs(int u){dfn[u]=++tim;for(int v:son[u])dfs(v);low[u]=tim;}
    void Modify(int x,ll w){x=dfn[x];while(x<=tim)c[x]+=w,x+=lb(x);}
    ll Query(int x)
    {
        ll ret=0;int X;
        X=low[x];while(X)ret+=c[X],X-=lb(X);
        X=dfn[x]-1;while(X)ret-=c[X],X-=lb(X);
        return ret;
    }
    void Dijkstra(int l,int r,int S)
    {
        for(int i=l;i<=r;++i)dis[i]=1e18,vis[i]=false;
        dis[S]=0;Q.push(mp(dis[S],S));
        while(!Q.empty())
        {
            int u=Q.top().sd;Q.pop();
            if(vis[u])continue;vis[u]=true;
            for(pi x:E[u])
            {
                int v=x.sd;ll w=x.fr;
                if(v<l||v>r||dis[v]<=dis[u]+w)continue;
                dis[v]=dis[u]+w;fa[v]=u;Q.push(mp(dis[v],v));
            }
        }
        for(int i=l;i<=r;++i)if(i!=S)son[fa[i]].pb(i);
        dfs(S);
    }
}T[18][3];
void Build(int l,int r,int dep)
{
    int mid=(l+r)>>1;
    for(int i=0;i<n;++i)
        T[dep][i].Dijkstra(id[l][0],id[r][n-1],id[mid][i]);
    if(l<mid)Build(l,mid-1,dep+1);
    if(r>mid)Build(mid+1,r,dep+1);
}
ll mx;
void Calc(int l,int r,int dep,int x,int y)
{
    int mid=(l+r)>>1;
    for(int i=0;i<n;++i)
        mx=min(mx,T[dep][i].dis[x]+T[dep][i].dis[y]);
    if(x<=id[mid][n-1]&&y>=id[mid][0])return;
    if(y<id[mid][0])Calc(l,mid-1,dep+1,x,y);
    else Calc(mid+1,r,dep+1,x,y);
}
void Modify(int l,int r,int dep,int x,int y,ll w)
{
    int mid=(l+r)>>1;
    for(int i=0;i<n;++i)
        if(T[dep][i].dis[x]+T[dep][i].dis[y]==mx)
        {
            T[dep][i].Modify(x,w),T[dep][i].Modify(y,w);
            return;
        }
    if(y<id[mid][0])Modify(l,mid-1,dep+1,x,y,w);
    else Modify(mid+1,r,dep+1,x,y,w);
}
ll Query(int l,int r,int dep,int u)
{
    int mid=(l+r)>>1;ll ret=0;
    for(int i=0;i<n;++i)
    {
        ll s=T[dep][i].Query(u);
        if(id[mid][i]==u)s>>=1;
        ret+=s;
    }
    if(u<id[mid][0])ret+=Query(l,mid-1,dep+1,u);
    if(u>id[mid][n-1])ret+=Query(mid+1,r,dep+1,u);
    return ret;
}
int main()
{
    n=read();m=read();Q=read();
    for(int i=0;i<m;++i)
        for(int j=0;j<n;++j)id[i][j]=++tot;
    for(int i=0;i<n-1;++i)
        for(int j=0;j<m;++j)
        {
            int u=id[j][i],v=id[j][i+1];ll w=read();
            E[u].pb(mp(w,v));E[v].pb(mp(w,u));
        }
    for(int i=0;i<n;++i)
        for(int j=0;j<m-1;++j)
        {
            int u=id[j][i],v=id[j+1][i];ll w=read();
            E[u].pb(mp(w,v));E[v].pb(mp(w,u));
        }
    Build(0,m-1,0);
    while(Q--)
    {
        int opt=read();
        if(opt==1)
        {
            int x1=read()-1,y1=read()-1,x2=read()-1,y2=read()-1;ll w=read();
            int u=id[y1][x1],v=id[y2][x2];if(u>v)swap(u,v);
            mx=1e18;Calc(0,m-1,0,u,v);
            Modify(0,m-1,0,u,v,w);
        }
        else
        {
            int x=read()-1,y=read()-1,u=id[y][x];
            printf("%lld\n",Query(0,m-1,0,u));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/10457420.html

时间: 2024-10-03 16:26:44

【CodeChef】Querying on a Grid(分治,最短路)的相关文章

ZOJ 3781 Paint the Grid Reloaded (最短路)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3781 题意: 在n*m矩阵的图定义连通区域为x值或y值相同且颜色相同的连通,连通具有传递性 每次可以把一个连通区域颜色反转(O变X,X变O) 问把所有块的颜色变为X最小的步数 方法: 很巧妙的最短路问题,先建图,然后以每个顶点为起点,找单源最短路的最大的值(也就是最深的深度),然后这个值取min 建图:相邻的块连边权1的边(即:通过1次反转可以使得两个连通块变为一个连通块

hdu 4871 树的分治+最短路记录路径

/* 题意:给你一些节点和一些边,求最短路径树上是k个节点的最长的路径数. 解:1.求出最短路径树--spfa加记录 2.树上进行操作--树的分治,分别处理子树进行补集等运算 */ #include<stdio.h> #include<string.h> #include<stdlib.h> #include<algorithm> #include<iostream> #include<queue> #define ll __int6

退役前的做题记录5.0

退役前的做题记录5.0 出于某种原因新开了一篇. [CodeChef]Querying on a Grid 对序列建立分治结构,每次处理\((l,mid,r)\)时,以\(mid\)为源点建立最短路树,这样跨越\(mid\)的点对之间的最短路一定会经过\(mid\),因此两点之间的最短路径就可以描述成最短路树上的两段到根路径.对每棵最短路树处理\(dfs\)序,用树状数组维护权值修改即可. [Wannafly挑战赛4F]线路规划 类似SCOI2016萌萌哒一题,并查集\(f_{i,j}\)表示从

Educational Codeforces Round 62 做题记录

A. 题解: 发现就是找前缀 max = i 的点的个数,暴力扫一遍 1 #include<bits/stdc++.h> 2 #define ll long long 3 #define pii pair<int,int> 4 #define mp(a,b) make_pair(a,b) 5 using namespace std; 6 #define maxn 10005 7 int n; 8 int a[maxn]; 9 int main() 10 { 11 scanf(&qu

codechef Prime Distance On Tree(树分治+FFT)

题目链接:http://www.codechef.com/problems/PRIMEDST/ 题意:给出一棵树,边长度都是1.每次任意取出两个点(u,v),他们之间的长度为素数的概率为多大? 树分治,对于每个根出发记录边的长度出现几次,然后每次求卷积,用素数表查一下即可添加答案. 1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include

BZOJ 4016: [FJOI2014]最短路径树问题( 最短路 + 点分治 )

先跑出最短路的图, 然后对于每个点按照序号从小到大访问孩子, 就可以搞出符合题目的树了. 然后就是经典的点分治做法了. 时间复杂度O(M log N + N log N) ---------------------------------------------------------------------------- #include<queue> #include<cctype> #include<cstdio> #include<cstring>

zoj 3781 Paint the Grid Reloaded (比较隐含的最短路)

Paint the Grid Reloaded Time Limit: 2 Seconds      Memory Limit: 65536 KB Leo has a grid with N rows and M columns. All cells are painted with either black or white initially. Two cells A and B are called connected if they share an edge and they are

Codechef SEP14 QRECT cdq分治+线段树

题意 支持删除矩阵.插入矩阵.查询当前矩阵与之前有多少个矩阵相交 算相交的时候容斥一下:相交矩形数 = 总矩形数-X轴投影不相交的矩形数-Y轴投影不相交的矩形数-XY轴投影下都不相交的矩形数 最后一项cdq分治解决 不是我的程序--->http://wyfcyx.is-programmer.com/posts/190325.html

Codechef Prime Distance On Tree(点分治+FFT)

题外话 最近做题发现自己非常SB,总是查一个SB错误查N久,简直绝望啊...弱逼为何而战 这次是忘记加long long查了N久..蛋碎无比 不过好歹是又做出一道cc hard的题了呢,感人肺腑 Description 题意很简单: 一棵树,问多少个二元组(u,v),满足u 到 v的路径长度为素数的概率为多少.所有边长度为1 Solution 自从重温了下 楼教的男人八题后,这种关于路径长度的题一看就是个点分治嘛 显然我们可以点分治统计路径长度,但是显然无法用two pointers或者数据结构