1. 程式人生 > 實用技巧 >【BZOJ4398】福慧雙修 題解(圖的建立)

【BZOJ4398】福慧雙修 題解(圖的建立)

題目連結

題目大意:給定一張$n$個點$m$條邊的無向圖,每條邊兩個方向的權值不一定相同。問從$1$出發不重複走一條邊回到$1$的最短路徑。

-------------------

暴力不太會。大概是$dfs$?複雜度不得上天……

正解:對於那些端點不是$1$的邊,因為要走最短路,所以這些邊只會走一次,所以對答案是沒有影響的。考慮端點為$1$的邊,我們進行“二進位制分組”。每次按照二進位制分為兩組:入邊和出邊,然後跑最短路。路徑長為$dis[edge[i].to]$加上入邊權值。這樣做能把所有情況包括進去,符合最優性質。

時間複雜度$O(n\log^2 n)$。

程式碼:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,vis[40005],dis[40005],tag[200005],ans=0x3f3f3f3f;
int head[200005],cnt=-1;
struct edge
{
    int next,to,dis;
}edge[200005];
struct node
{
    int dis,pos;
    bool operator < (const node &x) const
    {
        return x.dis<dis;
    }
};
priority_queue
<node> q; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void add(int from,int to,int dis) { edge[++cnt].next=head[from]; edge[cnt].to=to; edge[cnt].dis
=dis; head[from]=cnt; } inline void dijkstra() { for(int i=1;i<=n;i++) dis[i]=0x3f3f3f3f; memset(vis,0,sizeof(vis)); dis[1]=0;q.push((node){0,1}); while(!q.empty()) { node tmp=q.top();q.pop(); int now=tmp.pos; if (vis[now]) continue; vis[now]=1; for (int i=head[now];i!=-1;i=edge[i].next) { if (tag[i]==-1) continue; int to=edge[i].to; if (dis[to]>dis[now]+edge[i].dis) { dis[to]=dis[now]+edge[i].dis; if (!vis[to]) q.push((node){dis[to],to}); } } } for (int i=head[1];i!=-1;i=edge[i].next) if (tag[i]==-1&&ans>dis[edge[i].to]+edge[i^1].dis) ans=dis[edge[i].to]+edge[i^1].dis; } signed main() { n=read(),m=read(); memset(head,-1,sizeof(head)); for (int i=1;i<=m;i++) { int u=read(),v=read(),w1=read(),w2=read(); add(u,v,w1);add(v,u,w2); } for (int d=18;d>=0;d--) { for (int i=head[1];i!=-1;i=edge[i].next) if((i>>d)&1) tag[i]=0,tag[i^1]=-1; else tag[i]=-1,tag[i^1]=0; dijkstra(); for (int i=head[1];i!=-1;i=edge[i].next) if ((i>>d)&1) tag[i]=-1,tag[i^1]=0; else tag[i]=0,tag[i^1]=-1; dijkstra(); } printf("%lld",(ans==0x3f3f3f3f)?-1:ans); return 0; }