CSU - 1808 (湖南省赛16)
给定一张图,每条边有一个距离和颜色
问从 1到 n的最短路为多少
其中一条路径的长度不仅要算上距离
还要算上相邻两条边颜色的差
如果对每个点记录从某个颜色转移过来的最短路肯定是不现实的
以边为点跑最短路肯定也是不现实的,这种做法与前者无本质区别
但在CSUOJ上这两种做法都能过,可能是数据比较弱
正解是拆点,一个点每连出去一条边,就拆一个点出来
然后按颜色从小到大在相邻的内点中连上边,边权为颜色差
然后其他点按原有距离为边权连边
最终就转化为多源多汇的最短路
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <string>
#include <complex>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define SQR(a) ((a)*(a))
#define PCUT puts("\n----------")
const int maxn=1e5+10;
struct Graph
{
int ndn,edn,last[3*maxn];
int u[5*maxn],v[5*maxn],w[5*maxn],nxt[5*maxn];
void init(int _n){ndn=_n; edn=0; MST(last,-1);}
void adde(int _u, int _v, int _w)
{
u[edn]=_u; v[edn]=_v; w[edn]=_w;
nxt[edn]=last[_u];
last[_u]=edn++;
}
};
struct data
{
int v, c, w;
data(){}
data(int _v,int _c,int _w):v(_v),c(_c),w(_w){}
bool operator < (const data &rhs) const
{
if(c != rhs.c) return c < rhs.c;
if(v != rhs.v) return v < rhs.v;
if(w != rhs.w) return w < rhs.w;
return 0;
}
};
int N,M;
Graph G;
vector<data> in[maxn];
vector<int> id[maxn];
LL dist[3*maxn];
bool done[3*maxn];
LL dijkstra(int,int);
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
while(~scanf("%d%d", &N, &M))
{
for(int i=1; i<=N; i++) in[i].clear(), id[i].clear();
for(int i=0,u,v,c,w; i<M; i++)
{
scanf("%d%d%d%d", &u, &v, &c, &w);
in[u].push_back({v,c,w}); in[v].push_back({u,c,w});
}
for(int i=1; i<=N; i++) sort(in[i].begin(), in[i].end());
int tot=2;
for(int i=1; i<=N; i++) for(int j=0; j<(int)in[i].size(); j++) id[i].push_back(++tot);
G.init(tot);
for(int i=0; i<(int)in[1].size(); i++) G.adde(1, id[1][i], 0);
for(int i=0; i<(int)in[N].size(); i++) G.adde(id[N][i], 2, 0);
for(int i=1; i<=N; i++) for(int j=0; j<(int)in[i].size(); j++)
{
int v=in[i][j].v, c=in[i][j].c, w=in[i][j].w;
int tid=lower_bound(in[v].begin(), in[v].end(), data(i,c,w))-in[v].begin();
G.adde(id[v][tid], id[i][j], w);
if(j)
{
G.adde(id[i][j-1], id[i][j], abs(in[i][j-1].c - in[i][j].c));
G.adde(id[i][j], id[i][j-1], abs(in[i][j-1].c - in[i][j].c));
}
}
// for(int i=0; i<G.edn; i++) printf("%d->%d %d\n", G.u[i], G.v[i], G.w[i]);
cout << dijkstra(1,2) << "\n";
}
return 0;
}
struct dstr
{
int u; LL d;
bool operator < (const dstr &rhs) const {return d > rhs.d;}
};
LL dijkstra(int sta, int end)
{
priority_queue<dstr> pq;
pq.push({sta,0});
MST(dist,0x3f);
dist[sta]=0;
while(pq.size())
{
int u=pq.top().u;
LL d=pq.top().d;
pq.pop();
if(d>dist[u]) continue;
for(int e=G.last[u],v; ~e; e=G.nxt[e])
{
v=G.v[e];
if(dist[u]+G.w[e] < dist[v])
{
dist[v] = dist[u]+G.w[e];
pq.push({v,dist[v]});
}
}
}
return dist[end];
}
一种未能AC的做法
以下是我自己YY的一种做法,是基于暴力的
按普通的建图跑普通的最短路,那么对于一个点
需要枚举所有其他入边,然后再转移出去
所以可以离散化一下入边,然后用数状数组维护一下需要枚举的值中最小的
这样就能log更新,log查询,
总的时间复杂度是
(kE∗n)
的,空间复杂度是
(n)
的
但是由于这样貌似只能用SPFA转移,
而数据似乎正好有卡SPFA的,所以我的做法 TLE了。不
过我本地随机生成了几组数据,和标程对拍了一下,
似乎正确性是没有问题的。
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <string>
#include <complex>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define SQR(a) ((a)*(a))
#define PCUT puts("\n----------")
const int maxn=1e5+10;
const LL INF=0x3f3f3f3f3f3f3f3f;
struct Graph
{
int ndn,edn,last[maxn];
int u[2*maxn], v[2*maxn], c[2*maxn], w[2*maxn], nxt[2*maxn];
void init(int _n){ndn=_n; edn=0; MST(last,-1);}
void adde(int _u, int _v, int _c, int _w)
{
u[edn]=_u; v[edn]=_v; c[edn]=_c; w[edn]=_w;
nxt[edn]=last[_u];
last[_u]=edn++;
}
};
struct Dist
{
int siz;
LL ans;
vector<int> c;
vector<LL> bit[2];
vector<LL> tmin[2];
void clear(){ans = INF; c.clear(); bit[0].clear(); bit[1].clear();}
void init()
{
sort(c.begin(), c.end());
siz = unique(c.begin(), c.end())-c.begin();
c.resize(siz);
bit[0].resize(siz+1,INF);
bit[1].resize(siz+1,INF);
tmin[0].resize(siz+1,INF);
tmin[1].resize(siz+1,INF);
}
void add(int _n){c.push_back(_n);}
int id(int _n){return lower_bound(c.begin(), c.end(), _n)-c.begin()+1;}
int query(int x)
{
int tid=id(x);
LL res=INF;
for(int i=tid; i>0; i-=i&-i) res = min(res, bit[0][i]+x);
for(int i=siz-tid+1; i>0; i-=i&-i) res = min(res, bit[1][i]-x);
return res;
}
int update(int x, LL v)
{
int tid=id(x);
bool ok=0;
if(v-x<tmin[0][tid]) tmin[0][tid] = v-x, ok=1;
if(v+x<tmin[0][siz-tid+1]) tmin[1][siz-tid+1] = v+x, ok=1;
if(!ok) return 0;
for(int i=tid; i<=siz; i+=i&-i) bit[0][i] = min(bit[0][i], v-x);
for(int i=siz-tid+1; i<=siz; i+=i&-i) bit[1][i] = min(bit[1][i], v+x);
return 1;
}
};
int N,M;
Graph G;
Dist dist[maxn];
bool inq[maxn];
LL SPFA();
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
while(~scanf("%d%d", &N, &M))
{
G.init(N);
for(int i=1; i<=N; i++) dist[i].clear();
for(int i=0,u,v,c,w; i<M; i++)
{
scanf("%d%d%d%d", &u, &v, &c, &w);
G.adde(u,v,c,w); G.adde(v,u,c,w);
dist[u].add(c); dist[v].add(c);
}
for(int i=1; i<=N; i++) dist[i].init();
cout << SPFA() << "\n";
}
return 0;
}
LL SPFA()
{
CLR(inq);
queue<int> que;
que.push(1);
inq[1]=1;
dist[1].ans = 0;
for(auto c:dist[1].c) dist[1].update(c, 0);
while(que.size())
{
int u=que.front(); que.pop();
for(int e=G.last[u],v,c,w; ~e; e=G.nxt[e])
{
v = G.v[e]; c = G.c[e], w = G.w[e];
LL d = dist[u].query(c) + w;
// printf("%d->%d %lld\n", u, v, d);
// dist[v].update(c, d);
if(dist[v].update(c,d) || dist[v].ans > d)
{
dist[v].ans = min(dist[v].ans, d);
if(!inq[v])
{
inq[v] = 1;
que.push(v);
}
}
}
inq[u] = 0;
}
return dist[N].ans;
}