SRM615D1Med LongLongTripDiv1
これは良問かつ教育的だなぁ.
問題概要(原文)
$N$ 頂点 $M$ 本の無向辺からなる無向グラフがある.頂点 $0$ から 頂点 $N-1$ へのウォークであって,長さ $T$ のものはあるか?
考察
自明な考察として,連結でない場合はダメ.
頂点 $0$ に長さ $M$ の閉路が生えている場合を考える. すると,この問題は頂点 $0$ から頂点 $N-1$ へのウォークであって,長さ $x(T \equiv x \; \text{mod}\;M)$ のウォークであって,$x \leq T$ を満たすものがあるか?という問題に帰着できる.
ここで $M$ は頂点 $0$ から生えている適当な辺の行き来としてもよい.
あとは拡張グラフでダイクストラをするだけ.
ソースコード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Debug = System.Diagnostics.Debug;
using StringBuilder = System.Text.StringBuilder;
using Number = System.Int64;
public class LongLongTripDiv1
{
public string isAble(int n, int[] from, int[] to, int[] cost, long T)
{
var G = Enumerate(n, x => new List<KeyValuePair<int, int>>());
for (int i = 0; i < from.Length; i++)
{
G[from[i]].Add(new KeyValuePair<int, int>(to[i], cost[i]));
G[to[i]].Add(new KeyValuePair<int, int>(from[i], cost[i]));
}
return f(n, G, T) ? "Possible" : "Impossible";
}
bool f(int n, List<KeyValuePair<int, int>>[] G, long T)
{
if (G[0].Count == 0) return false;
var mod = G[0][0].Value * 2;
var dp = Enumerate(n, x => new long[mod]);
for (int i = 0; i < n; i++)
for (int j = 0; j < mod; j++)
dp[i][j] = long.MaxValue;
var pq = new RadixHeapPriorityQueue<KeyValuePair<int, long>>(x => x.Value);
pq.Enqueue(new KeyValuePair<int, long>(0, 0));
while (pq.Count > 0)
{
var p = pq.Dequeue();
var u = p.Key / mod;
var v = p.Key % mod;
var c = p.Value;
foreach (var e in G[u])
{
var to = e.Key;
var nv = (v + e.Value) % mod;
var nc = c + e.Value;
if (dp[to][nv] > nc)
{
dp[to][nv] = nc;
pq.Enqueue(new KeyValuePair<int, long>(to * mod + nv, nc));
}
}
}
return dp[n - 1][T % mod] <= T;
}
static public T[] Enumerate<T>(int n, Func<int, T> f) { var a = new T[n]; for (int i = 0; i < n; ++i) a[i] = f(i); return a; }
}
#region RadixHeap<T>
/// <summary>
/// 突っ込むのが整数でないとき用
/// </summary>
public class RadixHeapPriorityQueue<T>
{
/// <summary>
/// コストがlongのときは64
/// </summary>
const int SIZE = 64;
Number last;
Func<T, Number> get;
public RadixHeapPriorityQueue(Func<T, Number> f)
{
Debug.Assert(
(sizeof(Number) == sizeof(long) && SIZE == 64) ||
(sizeof(Number) == sizeof(int) && SIZE == 32));
Debug.Assert(f != null);
for (int i = 0; i <= SIZE; i++)
v[i] = new FastLinkedList<T>();
get = f;
}
static int bsr(long x)
{
if (x == 0) return -1;
else
{
var n = 0;
if (x >> (n + 32) > 0) n += 32;
if (x >> (n + 16) > 0) n += 16;
if (x >> (n + 8) > 0) n += 8;
if (x >> (n + 4) > 0) n += 4;
if (x >> (n + 2) > 0) n += 2;
if (x >> (n + 1) > 0) n += 1;
return n;
}
}
FastLinkedList<T>[] v = new FastLinkedList<T>[SIZE + 1];
int size;
public void Enqueue(T item)
{
var x = get(item);
Debug.Assert(last <= x);
size++;
v[bsr(x ^ last) + 1].Add(item);
}
public T Dequeue()
{
if (v[0].Count == 0)
{
var i = 1;
while (v[i].Count == 0) i++;
var nlast = Number.MaxValue;
for (FastLinkedList<T>.Node n = v[i].last; n != null; n = n.par)
{
var val = get(n.val);
if (val < nlast) nlast = val;
}
while (v[i].Count > 0)
{
var val = v[i].Pop();
v[bsr(get(val) ^ nlast) + 1].Add(val);
}
last = nlast;
}
size--;
return v[0].Pop();
}
public int Count { get { return size; } }
public bool Any() { return size > 0; }
#region FastLinkedList<T>
private class FastLinkedList<TT>
{
int size;
public Node last;
public int Count { get { return size; } }
public void Add(TT item) { last = new Node(item, last); size++; }
public TT Pop() { var ret = last.val; last = last.par; size--; return ret; }
public class Node
{
public readonly TT val;
public readonly Node par;
public Node(TT v, Node p) { val = v; par = p; }
}
}
#endregion
}
#endregion
コメント
- この拡張グラフのアイデアはなかった…
- 任意の閉路 $1$ つについて考えるだけでよいというのもポイント