从这篇开始换字体。。
数据范围很小。。直接暴力DP之即可。。
感觉跟之前做的1597的DP很像,都是基于连续区间的DP,应该也可以用斜率优化。。
还感觉跟某次CodeVS模拟赛的题的一道变态题(多面体原谅我。。)很像。。只不过那道题最后是二分图匹配。。
题解详见代码注释。。我只想吐槽。。窝一遇到什么n m d k p都出来的题,就很容易打错变量名(又因为这WA了三四次!。。这回我一开始就写成了输出f[n](这些变量与题目意义不同...自己改了一下。。)以后,要么把变量名搞长一点、搞有意义一点;要么就再码和调的过程中小心小心再小心(简直非人哉。。
// BZOJ 1003
// 跟某道奇怪的变态的codevs月赛题(喂东西吃)很像。。
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int D=100+5, N=20+5, M=N*N*2+5, P=20*100*100+5;
#define rep(i,a,b) for (int i=a; i<=b; i++)
#define dep(i,a,b) for (int i=a; i>=b; i--)
#define read(x) scanf("%d", &x)
#define fill(a,x) memset(a, x, sizeof(a))
#define LL long long
int n, m, d, k, p, u, v, w, l, r;
LL f[D], cost[D][D];
struct Graph {
int s, from[M], to[M], pre[M], dis[M], last[N];
void init() { s=-1; fill(last, -1); }
void ine(int a, int b, int w) {
s++;
from[s]=a, to[s]=b, pre[s]=last[a], dis[s]=w;
last[a]=s;
}
void ine2(int a, int b, int w) {
ine(a, b, w);
ine(b, a, w);
}
} G;
#define reg(i,G,u) for (int i=G.last[u]; i!=-1; i=G.pre[i])
struct List_Pair {
int s, pre[P], a[P], b[P], las[N];
void init() { s=-1; fill(las, -1); }
void push_back(int x, int a_, int b_) {
s++;
pre[s]=las[x]; a[s]=a_; b[s]=b_;
las[x]=s;
}
} mes;
#define rel(i,L,x) for (int i=L.las[x]; i!=-1; i=L.pre[i])
struct Node {
int id, dis;
bool operator < (const Node x) const { return dis>x.dis; }
Node(int id_, int dis_) { id=id_; dis=dis_; }
};
bool done[N], ok[N]; // ok[i]表示当前时间区间i点是否可走
int Dijkstra(int l, int r) {
priority_queue<Node> Q;
int d[N];
fill(d, 0x3f); fill(done, false); fill(ok, true);
// 预处理ok[],只要当前时间区间有一天不可走,那么ok[i]就置为false
rep(i,2,n-1) rel(j,mes,i)
if (max(l, mes.a[j])<=min(r, mes.b[j])) { ok[i]=false; break; }
Q.push(Node(1,0)); d[1]=0;
while (!Q.empty()) {
Node Nx=Q.top(); Q.pop();
int x=Nx.id;
if (done[x]) continue;
done[x]=true;
reg(i,G,x) {
int y=G.to[i], w=G.dis[i];
if (!done[y] && d[y]>d[x]+w && ok[y]) {
d[y]=d[x]+w;
Q.push(Node(y, d[y]));
}
}
}
return d[n];
}
int main()
{
G.init(); mes.init();
scanf("%d%d%d%d", &d, &n, &k, &m);
rep(i,1,m) scanf("%d%d%d", &u, &v, &w), G.ine2(u, v, w);
read(p);
rep(i,1,p) scanf("%d%d%d", &u, &l, &r), mes.push_back(u, l, r);
// 预处理出cost[i][j]表示第i天到第j天不改变航线所需要的最小花费
// 即:当i~j天可走节点的并集可以保证能到达目的地时,则最短路跑一遍即可;否则为无穷大
rep(i,1,d)
rep(j,i,d)
cost[i][j]=Dijkstra(i, j);
// 对花费进行DP:设f[i]表示前i天的最少花费
// 我们枚举最后一次改变路线的时间j,则有f[i] = min { f[j] + cost[j][i] } + k
// 注意,还可以不更改路线,此时的花费为cost[1][i]
rep(i,1,d) {
f[i]=(LL)cost[1][i]*i;
rep(j,1,i-1) f[i]=min(f[i], f[j]+cost[j+1][i]*(i-j)+k);
}
printf("%lld\n", f[d]);
return 0;
}