1. 程式人生 > >【刷題】BZOJ 2069 [POI2004]ZAW

【刷題】BZOJ 2069 [POI2004]ZAW

Description

在Byte山的山腳下有一個洞穴入口. 這個洞穴由複雜的洞室經過隧道連線構成. 洞穴的入口是一條筆直通向“前面洞口”的道路. 隧道互相都不交叉(他們只在洞室相遇). 兩個洞室要麼就通過隧道連線起來,要麼就經過若干隧道間接的相連. 現在決定組織辦一個'King's of Byteotia Cup' 比賽. 參賽者的目標就是任意選擇一條路徑進入洞穴並儘快出來即可. 一條路徑必須經過除了“前面洞口”之外還至少要經過其他一個洞室.一條路徑中一個洞不能重複經過(除了“前面洞室”以外),類似的一條隧道也不能重複經過. 一個著名的洞穴探險家 Byteala 正準備參加這個比賽. Byteala 已經訓練了數月而且他已獲得了洞穴系統的一套詳細資料. 對於每條隧道他都詳細計算了從兩個方向經過所需要的時間. 經過一個洞室的時間很短可以忽略不記. 現在Byteala 向計算一條符合條件的最優路徑.

Input

第一行有兩個數n 和 m (3 <= n <= 5000, 3 <= m <= 10000) 分別表示洞室的數目以及連線他們的隧道的數目. 洞室從1 到 n編號. “前面洞室”的編號為1. 接下來m 行描述了所有的隧道. 每行四個整數a,b,c,d 表示從洞室a到洞室b需要c分鐘的時間,而從洞室b到洞室a需要d分鐘的時間, 1 <= a,b <= n, a <> b, 1 <= c,d <= 10000. 你可以假設符合要求的路徑肯定存在.

Output

輸出一行,最少需要多少時間完成比賽.

Sample Input

3 3
1 2 4 3
2 3 4 2
1 3 1 1

Sample Output

6

Solution

這是一個假二進位制分組
對一號點相連的點按照二進位制來分組
列舉二進位制,一種只能由1走向它,另一種只能由它走向1
然後每次都跑一邊Dijkstra就好了
因為任意兩個點一定在一個數的二進位制中在不同的分組,所以方案經過的兩個點一定會被列舉到

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define ft first
#define sd second
#define pb(a) push_back(a)
#define mp(a,b) std::make_pair(a,b)
#define ITR(a,b) for(auto a:b)
#define REP(a,b,c) for(register int a=(b),a##end=(c);a<=a##end;++a)
#define DEP(a,b,c) for(register int a=(b),a##end=(c);a>=a##end;--a)
const int MAXN=10000+10,MAXM=200000+10,inf=0x3f3f3f3f;
int n,m,e,beg[MAXN],nex[MAXM<<1],to[MAXM<<1],was[MAXM<<1],d[MAXN],dir[MAXN],ans=inf;
std::priority_queue< std::pair<int,int>,std::vector< std::pair<int,int> >,std::greater< std::pair<int,int> > > q;
namespace IO{
    const int Buffsize=1<<15,Output=1<<24;
    static char Ch[Buffsize],*S=Ch,*T=Ch;
    inline char getc()
    {
        return((S==T)&&(T=(S=Ch)+fread(Ch,1,Buffsize,stdin),S==T)?0:*S++);
    }
    static char Out[Output],*nowps=Out;
    inline void flush(){fwrite(Out,1,nowps-Out,stdout);nowps=Out;}
    template<typename T>inline void read(T&x)
    {
        x=0;static char ch;T f=1;
        for(ch=getc();!isdigit(ch);ch=getc())if(ch=='-')f=-1;
        for(;isdigit(ch);ch=getc())x=x*10+(ch-'0');
        x*=f;
    }
    template<typename T>inline void write(T x,char ch='\n')
    {
        if(!x)*nowps++='0';
        if(x<0)*nowps++='-',x=-x;
        static int sta[111],tp;
        for(tp=0;x;x/=10)sta[++tp]=x%10;
        for(;tp;*nowps++=sta[tp--]^48);
        *nowps++=ch;
    }
}
using namespace IO;
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void insert(int x,int y,int z)
{
    to[++e]=y;
    nex[e]=beg[x];
    beg[x]=e;
    was[e]=z;
}
inline void Dijstra(int s)
{
    memset(d,inf,sizeof(d));
    d[s]=0;
    q.push(mp(d[s],s));
    while(!q.empty())
    {
        std::pair<int,int> pr=q.top();
        q.pop();
        if(pr.ft!=d[pr.sd])continue;
        int x=pr.sd;
        for(register int i=beg[x];i;i=nex[i])
            if(to[i]==1)
            {
                if(dir[x])continue;
                if(d[n+1]>d[x]+was[i])d[n+1]=d[x]+was[i];
            }
            else
            {
                if(x==1&&!dir[to[i]])continue;
                if(d[to[i]]>d[x]+was[i])d[to[i]]=d[x]+was[i],q.push(mp(d[to[i]],to[i]));
            }
    }
}
inline void solve(int ps)
{
    for(register int i=beg[1];i;i=nex[i])dir[to[i]]=(to[i]>>ps)&1;
    Dijstra(1);
    chkmin(ans,d[n+1]);
    for(register int i=beg[1];i;i=nex[i])dir[to[i]]^=1;
    Dijstra(1);
    chkmin(ans,d[n+1]);
}
int main()
{
    read(n);read(m);
    REP(i,1,m)
    {
        int u,v,w1,w2;read(u);read(v);read(w1);read(w2);
        insert(u,v,w1);insert(v,u,w2);
    }
    REP(i,0,13)solve(i);
    write(ans,'\n');flush();
    return 0;
}