首页 > 其他分享 ># [NOI2018] 归程

# [NOI2018] 归程

时间:2024-12-27 19:31:51浏览次数:5  
标签:10 int Yazid leq dis NOI2018 节点 归程

P4768 [NOI2018] 归程

题目描述

本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定。

魔力之都可以抽象成一个 \(n\) 个节点、\(m\) 条边的无向连通图(节点的编号从 \(1\) 至 \(n\))。我们依次用 \(l,a\) 描述一条边的长度、海拔

作为季风气候的代表城市,魔力之都时常有雨水相伴,因此道路积水总是不可避免的。由于整个城市的排水系统连通,因此有积水的边一定是海拔相对最低的一些边。我们用水位线来描述降雨的程度,它的意义是:所有海拔不超过水位线的边都是有积水的。

Yazid 是一名来自魔力之都的 OIer,刚参加完 ION2018 的他将踏上归程,回到他温暖的家。Yazid 的家恰好在魔力之都的 \(1\) 号节点。对于接下来 \(Q\) 天,每一天 Yazid 都会告诉你他的出发点 \(v\) ,以及当天的水位线 \(p\)。

每一天,Yazid 在出发点都拥有一辆车。这辆车由于一些故障不能经过有积水的边。Yazid 可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。
需要特殊说明的是,第二天车会被重置,这意味着:

  • 车会在新的出发点被准备好。
  • Yazid 不能利用之前在某处停放的车。

Yazid 非常讨厌在雨天步行,因此他希望在完成回家这一目标的同时,最小化他步行经过的边的总长度。请你帮助 Yazid 进行计算。

数据范围与约定

所有测试点均保证 \(T\leq 3\),所有测试点中的所有数据均满足如下限制:

  • \(n\leq 2\times 10^5\),\(m\leq 4\times 10^5\),\(Q\leq 4\times 10^5\),\(K\in\left\{0,1\right\}\),\(1\leq S\leq 10^9\)。

  • 对于所有边:\(l\leq 10^4\),\(a\leq 10^9\)。

  • 任意两点之间都直接或间接通过边相连。

  • 强制在线

Solution:

kruskal 重构树魅力时刻。

其实如果写过 P4197 Peaks 的话那么这题几乎是纯送的。

先分析题意:

我们先求出原图上以1为起点的单源最短路,然后题目要求我们在一个联通图上求出从一个点 \(u\) 出发只经过满足 $ h > p$ 的边能到达的所有节点 \(v\) 中 \(dis_{v}\) 的最小值。

实现:

我们发现 $ h > p$ 这个条件能通过kruskal重构树来构造.我们建一颗以 \(h\) 构成小根堆的kruskal重构树,叶子节点存的是 \(dis\) 然后对于每次查询,我们从节点 \(u\) 一直倍增向上跳到满足$ h > p$且深度最小的节点 \(u'\) 然后答案就是节点 \(u'\) 子树下的 \(dis\) 的最小值。

至于如何查询 \(u'\) 的子树,这里我选的是在欧拉序上查 st表

Code:

#include<bits/stdc++.h>
const int N=4e5+5;
const int inf=1e9;
const int lg=20;
using namespace std;
int n,m,days,ans;
vector<tuple<int,int> > E[N];
vector<int> e[N<<1];
int val[N],dis[N];
struct Edge{
    int x,y,w;
    bool operator <(const Edge &e)const{
        return w>e.w;
    }
}q[N];
struct Kruskal{
    int fa[N],tot;
    int find(int x){return fa[x]= fa[x]==x ? fa[x] : find(fa[x]);}
    void add(int x,int y){e[x].emplace_back(y);}
    void build()
    {
        tot=n;
        for(int u=1;u<=n;u++)fa[u]=u;
        for(int i=1;i<=m;i++)
        {
            int x=q[i].x,y=q[i].y,w=q[i].w;
            int u=find(x),v=find(y);
            if(u!=v)
            {
                val[++tot]=w;
                add(tot,v);add(tot,u);
                //cout<<"add:"<<tot<<" : "<<u<<" "<<v<<"="<<w<<"\n";
                fa[tot]=fa[u]=fa[v]=tot;
            }
        }
    }

}K;
struct Dijkstra{
    struct Node{
        int x,val;
        bool operator<(const Node &n)const{
            return n.val<val;
        }
    };
    priority_queue<Node> Q;
    int vis[N];
    void init()
    {
        for(int i=1;i<=n;i++)dis[i]=inf,vis[i]=0;
    }
    void dijksta(int s)
    {
        init();
        dis[s]=0;Q.push({s,dis[s]});
        while(!Q.empty())
        {
            int x=Q.top().x;Q.pop();
            if(vis[x])continue;
            vis[x]=1;
            for(auto [y,w] : E[x])
            {
                if(dis[y]>dis[x]+w)
                {
                    dis[y]=dis[x]+w;
                    Q.push({y,dis[y]});
                }
            }
        }
    }
}D;
struct Graph{
    int dfn[N<<1];
    int f[N<<1][lg+5];
    int st[N<<1],ed[N<<1];
    void dfs(int x,int fa)
    {
        st[x]=++dfn[0];
        f[x][0]=fa;
        for(int j=1;j<=lg;j++)f[x][j]=f[f[x][j-1]][j-1];
        for(auto y : e[x])
        {
            if(y==fa)continue;
            dfs(y,x);
        }
        ed[x]=dfn[0];
    }
    int find(int x,int k)
    {
        for(int j=lg;j>=0;j--)if(val[f[x][j]]>k)x=f[x][j];
        return x;
    }
}G;
struct ST{
    int LG[N<<1];
    int st[N<<1][lg+5];
    int R;
    void init()
    {
        R=(n<<1)-1;
        for(int i=2;i<=R;i++)LG[i]=LG[i>>1]+1;
        for(int i=1;i<=R;i++)st[i][0]=inf;
        for(int i=1;i<=n;i++)st[G.st[i]][0]=dis[i];
    }
    void build()
    {
        init();
        //cout<<"R:"<<R<<"\n";
        //for(int i=1;i<=R;i++)cout<<st[i][0]<<(i==R ? "\n" : " ");
        for(int j=1;j<=lg;j++)for(int i=1;i<=R;i++)
        {
            int tmp=min(i+(1<<j-1),R);
            st[i][j]=min(st[i][j-1],st[tmp][j-1]);
        }
    }
    int query(int l,int r)
    {
        int k=LG[r-l+1];
        return min(st[l][k],st[r-(1<<k)+1][k]);
    }
}S;
struct Change{
    int k,s;
    int chage_v(int x){return (x+k*ans-1)%n+1;}
    int chage_p(int x){return (x+k*ans)%(s+1);}
}C;
void Clear()
{
    int R=(n<<1)-1;G.dfn[0]=0;
    for(int i=1;i<=n;i++)E[i].clear();
    for(int i=1;i<=R;i++)e[i].clear(),val[i];
}
void work()
{
    cin>>n>>m;
    ans=0;
    for(int i=1,x,y,w,h;i<=m;i++)
    {
        scanf("%d%d%d%d",&x,&y,&w,&h);
        E[x].emplace_back(y,w);
        E[y].emplace_back(x,w);
        q[i]={x,y,h};
    }
    sort(q+1,q+1+m);
    D.dijksta(1);
    K.build();
    G.dfs(K.tot,0);
    S.build();
    cin>>days>>C.k>>C.s;
    for(int i=1,v,p;i<=days;i++)
    {
        scanf("%d%d",&v,&p);
        v=C.chage_v(v);p=C.chage_p(p);
        //cout<<"v p : "<<v<<" "<<p<<"\n";
        v=G.find(v,p);
        ans=S.query(G.st[v],G.ed[v]);
        //cout<<v<<"="<<G.st[v]<<" "<<G.ed[v]<<"\n";
        printf("%d\n",ans);
    }
    //cout<<"END\n";
    Clear();
}
int main()
{
    //freopen("P4768_2.in","r",stdin);freopen("P4768.out","w",stdout);
    int T;
    cin>>T;
    while(T--)
    work();
    return 0;
}

标签:10,int,Yazid,leq,dis,NOI2018,节点,归程
From: https://www.cnblogs.com/LG017/p/18636577

相关文章

  • 题解 LGP5397【[Ynoi2018] 天降之物】/ 第四分块
    题解P5397【[Ynoi2018]天降之物】/第四分块题目描述一个长为\(n\)的序列\(a\)。你需要实现\(m\)个操作,操作有两种:把序列中所有值为\(x\)的数的值变成\(y\)。找出一个位置\(i\)满足\(a_i=x\),找出一个位置\(j\)满足\(a_j=y\),使得\(|i-j|\)最小,并输出\(|i-......
  • #莫队二次离线,根号分治#洛谷 5398 [Ynoi2018] GOSICK
    题目\(m\)组询问求\(\sum_{l\leqi,j\leqr}[a_i\bmoda_j==0],n,m,a_i\leq5\times10^5\)分析设\(f(l,r,x)\)表示\(i或j\in[1,x],i或j\in[l,r]\)时的答案,\(g_x\)表示\([1,x]\)的答案,根号的做法可以通过三秒由于涉及区间内的求值,需要在莫队的基础上二次离线,那......
  • P4119 Ynoi2018 未来日记
    P4119Ynoi2018未来日记lxl出的题好duliu啊。感谢来自fr200110217102的博客题解P4119【Ynoi2018未来日记】。下标分块+值域分块+并查集其实一开始的方向应该是尝试线段树或者其它的动态维护的算法,直到时间复杂度和空间复杂度对不上,你才会想到——要分块!区间第\(k\)......
  • NOI2018 你的名字。
    您的名字。做法好像和其他题解不太一样。空间少个\(\log\),而且常数小。description给定长度为\(n\)的字符串\(S\)。\(Q\)次询问,第\(t\)次给定字符串\(T_t\)以及正整数\(l,r\in[1,n]\)。每次询问回答\(T_t\)有几个本质不同子串在\(S_{l\dotsr}\)中没有出现过......
  • P4786 [BalkanOI2018] Election 题解
    题意给定一个长度为\(n\)的字符串\(s\),有\(m\)个询问,每次询问最少需要删掉多少个字符才能使\(l\)到\(r\)组成的字符串当中的每一个前缀和后缀都满足C的数量不小于T的数量。思路因为要满足C的数量不小于T的数量,我们不妨设字符C的位置的值为\(1\),字符S的位......
  • P4119 [Ynoi2018] 未来日记
    \(\text{Links}\)LuoguBlogP4119[Ynoi2018]未来日记题外话个人生涯中第一道独立通过的Ynoi大分块!!同时也是个人生涯中通过的第十道Ynoi系列题目!!卡了好久结果加了个优化就过了/yunAC那一瞬间的场面好像56SecondsLater/ll感觉\(8.5\)的评分还是有点虚......
  • P4770 [NOI2018] 你的名字 做题记录
    我永远喜欢数据结构题目传送门给出字符串\(s\)以及\(q\)个询问,第\(i\)个询问给出一个串\(t_i\)以及一个区间\([l_i,r_i]\)。记\(s[l,r]\)为字符串\(s\)第\(l\)位到第\(r\)位字符顺次拼接而成的子串。形式化地,\(s[l,r]=\overline{s_ls_{l+1}\dotss_r}\)......
  • 【题解】P4768 [NOI2018] 归程 / Kruskal 重构树
    补补以前懒得总结的零碎东西。kruskal重构树使用条件:求无向图中两点之间所有路径的最大边权的最小值构造:依kruskal得到最小生成树从小到大考虑生成树中的边\((u,v)\)对于\((u,v)\),新建一个结点,作为重构树中\(u,v\)的父结点该结点的点权为\((u,v)\)的......
  • 【Ynoi2018】天降之物
    【Ynoi2018】天降之物题意给定一个长为\(n\)的序列\(a\),支持两种操作:将所有\(a_p=x\)修改为\(y\)。查询\(\min(|i-j|)\),满足\(a_i=x\anda_j=y\)或者\(a_i=y\anda_j=x\)。题解考虑序列分块,首先考虑块内贡献,设块长为\(B\),由于分块的\(B\)一......
  • 「突刺贯穿第二分块」P4117 [Ynoi2018] 五彩斑斓的世界
    很帅气!分块在线转离线,考虑每个块对于询问的贡献。维护块的max和tag分别代表最大值和减了多少。先考虑整块,\(max<2*x:\)每次暴力区间平移即可。否则对于\([1,x]\)全部加上\(x\)平移到\([x+1,x*2]\),然后区间整体减\(x\)即可。散块怎么做?暴力减,然后重构块......