Luogu P4768 [NOI2018]归程

题目链接

题目大意:一个n个节点、m条边的无向连通图。我们依次用l,a描述一条边的长度、海拔。对于接下来Q个询问,每一天Yazid都会告诉你他的出发点v,Yazid需要从v到1,以及当天的水位线p,所有海拔不超过水位线的边都是有积水的。

每一个询问,Yazid在出发点都拥有一辆车。这辆车不能经过有积水的边。Yazid可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。

需要特殊说明的是,第二天车会被重置,这意味着:

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

最小化Yazid的步行距离,强制在线

NOI2018的毒瘤题目,本以为自己能凎出来,但是还是看了题解,妙啊

对于最优解(1,v)这条路径中,一定存在一个点u,使得(1,u)全为步行,(u,v)全为开车,由此我们可以暴力枚举分界点u

但是怎么处理积水的边呢?这是一个经典的问题,形如从点v开始只经过边权小于(或大于)x的路径所能到达节点集合,可以用kruskal重构树。

 


 

在kruskal重构树中,从点v倍增向上跳,找到深度最小的一个点权大于p的节点,p的子树就是v可达的子集。

预处理出1到所有点的最短路,再预处理出重构树里每个子树内到1最短节点的距离就OK了。

还有一件事,关于SPFA,它死了

 

#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
inline int read() {
    char ch;
    int bj=1;
    while(!isdigit(ch=getchar()))
        bj=(ch=='-')?-1:1;
    int res=ch^(3<<4);
    while(isdigit(ch=getchar()))
        res=(res<<1)+(res<<3)+(ch^(3<<4));
    return res*bj;
}
void printnum(int x) {
    if(x>9)printnum(x/10);
    putchar(x%10+'0');
}
inline void print(int x,char ch) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    printnum(x);
    putchar(ch);
}
const int MAXN=2e5+5;
int n,m,tot,cnt,g[MAXN<<1][25],cnt1;
int lastans;
int a[MAXN<<1];
int h[MAXN];
int h1[MAXN<<1];
struct Edge {
    int to,nxt,v;
} w[MAXN<<2]; //图里的边
struct node {
    int to,nxt;
} w1[MAXN<<2]; //树里的边
int d[MAXN<<1];
bool vst[MAXN];//最短路
int prt[MAXN<<1];//并查集
int f[MAXN<<1];
priority_queue<pair<int,int> >q;
struct node1 {
    int x,y,a;
    inline bool operator < (node1 b)const {
        return a>b.a;
    }
} e[MAXN<<1];
inline void AddEdge(int x,int y,int z) {//建图边
    w[++cnt].to=y;
    w[cnt].nxt=h[x];
    w[cnt].v=z;
    h[x]=cnt;
}
inline void AddEdge2(int x,int y) {//建树边
    w1[++cnt1].to=y;
    w1[cnt1].nxt=h1[x];
    h1[x]=cnt1;
}
inline void dijkstra(int v0) {
    memset(d,0x3f,sizeof(d));
    memset(vst,0,sizeof(vst));
    d[v0]=0;
    q.push(make_pair(0,v0));
    while(!q.empty()) {
        int x=q.top().second;
        q.pop();
        if(vst[x])continue;
        vst[x]=1;
        for(int i=h[x]; i; i=w[i].nxt) {
            int v=w[i].to;
            if(d[v]>d[x]+w[i].v) {
                d[v]=d[x]+w[i].v;
                q.push(make_pair(-d[v],v));
            }
        }
    }
}
int GetFather(int x) {
    return x^prt[x]?prt[x]=GetFather(prt[x]):x;
}
inline void kruskal() {
    sort(e+1,e+m+1);
    tot=n;
    for(int i=1; i<=n; i++)prt[i]=i;
    for(int i=1; i<=m; i++) {
        int x=GetFather(e[i].x),y=GetFather(e[i].y);
        if(x^y) {
            tot++;
            a[tot]=e[i].a;
            prt[x]=prt[y]=prt[tot]=tot;
            AddEdge2(x,tot);
            AddEdge2(tot,x);
            AddEdge2(y,tot);
            AddEdge2(tot,y);
        }
    }
}
inline void DFS(int x,int fa) {
    f[x]=d[x];
    g[x][0]=fa;
    for(int i=h1[x]; i; i=w1[i].nxt) {
        int v=w1[i].to;
        if(v==fa)continue;
        DFS(v,x);
        f[x]=min(f[x],f[v]);
    }
}
inline void ST() {
    for(int j=1; (1<<j)<=tot; j++)
        for(int i=1; i<=tot; i++)
            if(g[i][j-1])
                g[i][j]=g[g[i][j-1]][j-1];
}
inline void clear() {
    memset(h,0,sizeof(h));
    memset(w,0,sizeof(w));
    memset(h1,0,sizeof(h1));
    memset(w1,0,sizeof(w1));
    cnt=cnt1=0;
    memset(g,0,sizeof(g));
    memset(prt,0,sizeof(prt));
    lastans=0;
}
inline void Solve() {
    n=read();
    m=read();
    int x,y,z;
    for(int i=1; i<=m; i++) {
        e[i].x=read();
        e[i].y=read();
        z=read();
        e[i].a=read();
        AddEdge(e[i].x,e[i].y,z);
        AddEdge(e[i].y,e[i].x,z);
    }
    dijkstra(1);
    kruskal();
    DFS(tot,0);
    ST();
    int Q=read();
    int k=read(),s=read();
    while(Q--) {
        x=(read()+k*lastans-1)%n+1;
        y=(read()+k*lastans)%(s+1);
        for(int i=24; ~i; i--)
            if(g[x][i]&&a[g[x][i]]>y)x=g[x][i];
        print(lastans=f[x],'\n');
    }
}
signed main() {
    int T=read();
    while(T--) {
        clear();
        Solve();
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/soledadstar/p/11553336.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值