1. 程式人生 > >[BZOJ 4144] Petrol

[BZOJ 4144] Petrol

gre off pri prior 學會 tro std problem names

Link:

BZOJ 4144 傳送門

Solution:

一道不錯的圖論綜合題

因為只詢問關鍵點,因此重點是要求出關鍵點之間的最短路,以最短路建圖

記$nst[i]$為離$i$最近的關鍵點:可以發現$A->B$的最短路徑上,一定是前一半$nst[i]$為$A$,後一半$nst[i]$為$B$

否則設$nst[x]=C$,則$A->C->B$由於經過了$C$的加油一定不會更差

因此對於符合條件的$A->B$連邊就建出了新圖(如果離線的話僅修改權值不更改節點也可行)

接下來為了使連通的最大邊最小跑$Kruskal$,此時分為兩種方式:

Solution A:Offline

明顯是更簡單的解法,學會利用離線啊!

將$Query$按$b$從小到大排序

每次只連接$edge(u,v) (w(u,v)<=b)$,最後判斷$x,y$是否在一個點集中即可

Solution B:Online

如果要強制在線也可以做,而且算常用套路

先跑$Kruskal$,將圖轉化為了樹,每次詢問在樹上倍增查找

Code:

技術分享圖片
#include<bits/stdc++.h>

using namespace std;
typedef pair<int,int> P;
const int MAXN=2e5+10;
struct data{int x,y,w;}edge[MAXN];
struct E{int to,nxt,w;}e[MAXN<<2]; priority_queue<P,vector<P>,greater<P> > pq; int belong[MAXN],f[MAXN][25],mx[MAXN][25],dep[MAXN],nst[MAXN],cnt=0; int n,m,s,q,tot,head[MAXN],pos[MAXN],dist[MAXN],res[MAXN],vis[MAXN],fa[MAXN]; void add_edge(int from,int to,int cost) { e[++tot].nxt=head[from
];e[tot].w=cost;e[tot].to=to;head[from]=tot; e[++tot].nxt=head[to];e[tot].w=cost;e[tot].to=from;head[to]=tot; } int find(int x){return (fa[x]==x)?x:(fa[x]=find(fa[x]));} bool cmp(data a,data b){return a.w<b.w;} void dijkstra() { for(int i=1;i<=s;i++) dist[pos[i]]=0,nst[pos[i]]=pos[i],pq.push(P(0,pos[i])); while(!pq.empty()) { int u=pq.top().second;pq.pop(); if(vis[u]) continue;vis[u]=true; for(int i=head[u];i;i=e[i].nxt) if(dist[e[i].to]>dist[u]+e[i].w) { dist[e[i].to]=dist[u]+e[i].w;nst[e[i].to]=nst[u]; pq.push(P(dist[e[i].to],e[i].to)); } } } void MST() { memset(head,0,sizeof(head)); int e_cnt=0;tot=0; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) { int x=edge[i].x,y=edge[i].y; if(nst[x]==nst[y]) continue; edge[++e_cnt].w=dist[x]+dist[y]+edge[i].w; edge[e_cnt].x=nst[x];edge[e_cnt].y=nst[y]; } sort(edge+1,edge+e_cnt+1,cmp); for(int i=1;i<=e_cnt;i++) { int fx=find(edge[i].x),fy=find(edge[i].y); if(fx!=fy) fa[fx]=fa[fy],add_edge(edge[i].x,edge[i].y,edge[i].w); } } void dfs(int x,int bloc) { belong[x]=bloc; for(int i=1;(1<<i)<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1], mx[x][i]=max(mx[x][i-1],mx[f[x][i-1]][i-1]); for(int i=head[x];i;i=e[i].nxt) { if(e[i].to==f[x][0]) continue; dep[e[i].to]=dep[x]+1; f[e[i].to][0]=x;mx[e[i].to][0]=e[i].w; dfs(e[i].to,bloc); } } int check(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int t=dep[x]-dep[y],ret=0; for(int i=0;i<=20;i++) if(t&(1<<i)) ret=max(ret,mx[x][i]),x=f[x][i]; for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) ret=max(ret,max(mx[x][i],mx[y][i])), x=f[x][i],y=f[y][i]; if(x!=y) ret=max(ret,max(mx[x][0],mx[y][0])); return ret; } int main() { scanf("%d%d%d",&n,&s,&m); for(int i=1;i<=s;i++) scanf("%d",&pos[i]); for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w), add_edge(edge[i].x,edge[i].y,edge[i].w); memset(dist,0x3f,sizeof(dist)); dijkstra();MST(); for(int i=1;i<=s;i++) if(!belong[pos[i]]) dfs(pos[i],++cnt); scanf("%d",&q); for(int i=1;i<=q;i++) { int x,y,b;scanf("%d%d%d",&x,&y,&b); if(belong[x]!=belong[y]) puts("NIE"); else puts((check(x,y)<=b)?"TAK":"NIE"); } return 0; }
Online 技術分享圖片
#include<bits/stdc++.h>

using namespace std;
typedef pair<int,int> P;
const int MAXN=2e5+10;
struct data{int x,y,w;}edge[MAXN];
struct E{int to,nxt,w;}e[MAXN<<2];
struct Query{int x,y,b,id;}q[MAXN];
priority_queue<P,vector<P>,greater<P> > pq;
int n,m,s,tot,head[MAXN],pos[MAXN],dist[MAXN],f[MAXN],res[MAXN],vis[MAXN];

void add_edge(int from,int to,int cost)
{
    e[++tot].nxt=head[from];e[tot].w=cost;e[tot].to=to;head[from]=tot;
    e[++tot].nxt=head[to];e[tot].w=cost;e[tot].to=from;head[to]=tot;
}

int find(int x){return (f[x]==x)?x:(f[x]=find(f[x]));}
bool cmp1(data a,data b){return a.w<b.w;}
bool cmp2(Query a,Query b){return a.b<b.b;}

void dijkstra()
{
    for(int i=1;i<=s;i++)
        dist[pos[i]]=0,pq.push(P(0,pos[i]));
    while(!pq.empty())
    {
        P t=pq.top();pq.pop();
        int u=t.second;
        if(t.first>dist[u]) continue; //這裏的判斷一定要加,或用vis也行 
        for(int i=head[u];i;i=e[i].nxt) //否則瘋狂TLE 
            if(dist[e[i].to]>dist[u]+e[i].w)
                dist[e[i].to]=dist[u]+e[i].w,pq.push(P(dist[e[i].to],e[i].to));
    }
}

int main()
{
    scanf("%d%d%d",&n,&s,&m);
    for(int i=1;i<=s;i++) scanf("%d",&pos[i]);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w),
        add_edge(edge[i].x,edge[i].y,edge[i].w);
    
    memset(dist,0x3f,sizeof(dist));dijkstra();
    for(int i=1;i<=m;i++)
        edge[i].w+=dist[edge[i].x]+dist[edge[i].y];
    
    scanf("%d",&s);
    for(int i=1;i<=s;i++)
        scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].b),q[i].id=i;
    sort(edge+1,edge+m+1,cmp1);
    sort(q+1,q+s+1,cmp2);
    
    int cur=1;
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=s;i++)
    {
        while(cur<=m&&edge[cur].w<=q[i].b)
        {
            int fx=find(edge[cur].x),fy=find(edge[cur].y);
            if(fx!=fy) f[fx]=fy;cur++;
        }
        res[q[i].id]=(find(q[i].x)==find(q[i].y));
    }
    for(int i=1;i<=s;i++)
        puts(res[i]?"TAK":"NIE");
    return 0;
}
Offline

Review:

1、關於$dijkstra$的模板:

一定要保證每個點只對其後繼更新一次!不管是用$vis[i]$還是$top.first>dist[top.second]$

2、如果可以離線,查看能否通過將詢問排序簡化查詢過程

3、套路:將圖轉為樹,然後樹上倍增查詢

[BZOJ 4144] Petrol