1. 程式人生 > >NOI2018 D1T1 [NOI2018]歸程 解題報告

NOI2018 D1T1 [NOI2018]歸程 解題報告

P4768 [NOI2018]歸程

題目描述

本題的故事發生在魔力之都,在這裡我們將為你介紹一些必要的設定。

魔力之都可以抽象成一個 \(n\) 個節點、\(m\) 條邊的無向連通圖(節點的編號從 \(1\)\(n\))。我們依次用 \(l,a\) 描述一條邊的長度、海拔。

作為季風氣候的代表城市,魔力之都時常有雨水相伴,因此道路積水總是不可避免的。由於整個城市的排水系統連通,因此有積水的邊一定是海拔相對最低的一些邊。我們用水位線來描述降雨的程度,它的意義是:所有海拔不超過水位線的邊都是有積水的。

Yazid是一名來自魔力之都的OIer,剛參加完ION2018的他將踏上歸程,回到他溫暖的家。

Yazid 的家恰好在魔力之都的 \(1\) 號節點。對於接下來 \(Q\) 天,每一天Yazid 都會告訴你他的出發點 \(v\) ,以及當天的水位線\(p\)。 每一天,Yazid 在出發點都擁有一輛車。這輛車由於一些故障不能經過有積水的邊。 Yazid 可以在任意節點下車,這樣接下來他就可以步行經過有積水的邊。但車會被留在他下車的節點並不會再被使用。

需要特殊說明的是,第二天車會被重置,這意味著:

  • 車會在新的出發點被準備好。
  • Yazid 不能利用之前在某處停放的車。Yazid 非常討厭在雨天步行,因此他希望在完成回家這一目標的同時,最小化他步行經過的邊的總長度。

請你幫助 Yazid 進行計算。

本題的部分測試點將強制線上,具體細節請見【輸入格式】和【子任務】。

輸入輸出格式

輸入格式:

單個測試點中包含多組資料。輸入的第一行為一個非負整數\(T\),表示資料的組數。

接下來依次描述每組資料,對於每組資料:

第一行 \(2\) 個非負整數 \(n,m\),分別表示節點數、邊數。

接下來 \(m\) 行,每行 \(4\) 個正整數\(u, v, l, a\),描述一條連線節點 \(u, v\) 的、長度為 \(l\)、海拔為 \(a\) 的邊。 在這裡,我們保證\(1 \leq u,v \leq n\)

接下來一行 \(3\) 個非負數 \(Q, K, S\) ,其中 \(Q\)

表示總天數,\(K \in {0,1}\) 是一個會在下面被用到的係數,\(S\) 表示的是可能的最高水位線。

接下來 \(Q\) 行依次描述每天的狀況。每行 \(2\) 個整數 \(v_0,p_0\)描述一天:

這一天的出發節點為

\(v = (v_0 + K \times \mathrm{lastans} - 1) \bmod n + 1\)

這一天的水位線為

\(p = (p_0 + K \times \mathrm{lastans}) \bmod (S + 1)\)

其中 lastans 表示上一天的答案(最小步行總路程)。特別地,我們規定第 \(1\) 天時 lastans = 0。 在這裡,我們保證\(1 \leq v_0 \leq n,0 \leq p_0 \leq S\)

對於輸入中的每一行,如果該行包含多個數,則用單個空格將它們隔開。

輸出格式:

依次輸出各組資料的答案。對於每組資料:

輸出 \(Q\) 行每行一個整數,依次表示每天的最小步行總路程。

所有測試點均保證 \(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\)


如果在NOI考場上,這題一定不能掛。

如果你沒學過蟲狗鼠或者並茶几,那麼只要你不寫SPFA,在一個多個小時內穩穩拿到離線的65pts是沒問題的。

可持久化並茶几我不會,不過蟲狗鼠的思路挺簡單的,會就是一眼題。

思路:
先按最大生成樹建重構樹,按照慣例順手搞一下倍增,然後預處理1為源點的最短路,每次詢問直接倍增找到大於水位的節點更新答案即可。


Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
const int N=4e5+10;
struct Edge
{
    int u,v,w;
    bool friend operator <(Edge n1,Edge n2){return n1.w>n2.w;}
}e[N];
int head[N],to[N<<1],Next[N<<1],edge[N<<1],cnt;
void add(int u,int v,int w)
{
    to[++cnt]=v,edge[cnt]=w,Next[cnt]=head[u],head[u]=cnt;
}
int F[N],f[N][20],n,m;
int Find(int x){return F[x]=F[x]==x?x:Find(F[x]);}
int dis[N<<1],used[N],poi[N<<1];
int min(int x,int y){return x<y?x:y;}
#define P std::pair <int,int>
std::priority_queue <P,std::vector <P>,std::greater <P> > q;
void disj()
{
    memset(dis,0x3f,sizeof(dis));
    memset(used,0,sizeof(used));
    dis[1]=0;
    q.push(std::make_pair(0,1));
    while(!q.empty())
    {
        int u=q.top().second;
        q.pop();
        if(used[u]) continue;
        used[u]=1;
        for(int i=head[u];i;i=Next[i])
        {
            int v=to[i];
            if(dis[v]>dis[u]+edge[i])
            {
                dis[v]=dis[u]+edge[i];
                q.push(std::make_pair(dis[v],v));
            }
        }
    }
}
int find(int v,int p)
{
    for(int i=19;~i;i--)
        if(poi[f[v][i]]>p)
            v=f[v][i];
    return dis[v];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(head,0,sizeof(head));
        memset(f,0,sizeof(f));
        memset(poi,0,sizeof(poi));
        cnt=0;
        scanf("%d%d",&n,&m);
        int n_=n;
        for(int w,i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&e[i].u,&e[i].v,&w,&e[i].w);
            add(e[i].u,e[i].v,w),add(e[i].v,e[i].u,w);
        }
        for(int i=1;i<=n<<1;i++) F[i]=i;
        disj();
        std::sort(e+1,e+1+m);
        for(int i=1;i<=m;i++)
        {
            int u=e[i].u,v=e[i].v;
            int r1=Find(u),r2=Find(v);
            if(r1==r2) continue;
            F[r1]=F[r2]=f[r1][0]=f[r2][0]=++n;
            poi[n]=e[i].w,dis[n]=min(dis[r1],dis[r2]);
        }
        for(int j=1;j<=19;j++)
            for(int i=1;i<=n;i++)
                f[i][j]=f[f[i][j-1]][j-1];
        int lastans=0,q,k,s;
        scanf("%d%d%d",&q,&k,&s);
        for(int v,p,i=1;i<=q;i++)
        {
            scanf("%d%d",&v,&p);
            v=(v+k*lastans-1)%n_+1;
            p=(p+k*lastans)%(s+1);
            printf("%d\n",lastans=find(v,p));
        }
    }
    return 0;
}

2018.10.17