【LGR-101】洛谷 2 月月赛 I & WdOI R1.5 / RdOI R3.5 Div.2 C [旅人 1977] 题解
糟糕的赛时评测体验,糟糕的适合小常数人做法,糟糕的我的大常数。
如果出题人和我一个做法,我把出题人鲨了
要求最短路,权值计算很复杂。
考虑权值计算为什么复杂,发现问题在于走过后面的边会影响的前面的边的总贡献,具有后效性。
正难则反,我们考虑倒着走,我们可以通过打标记的方式知道哪些点一旦打标记之后会 push_down 多少次,从而达到从正向看来是费用提前计算的效果,于是每一步路的花费与且仅与之前走过的边有关。
也就是与我们打上的标记有关。考虑打标记方案数,显然等价于对非叶子点随意染色而根节点所在连通块的种类的方案数,可以通过简单的 dp 计算。
\[dp_1 = 0 , dp_2 = 1 \\
dp_i = 1 + dp_{i / 2} + dp_{i - i / 2} + dp_{i / 2} \times dp_{i - i / 2}
\]
\(K\) 个数的线段树的标记方案数显然为 \(dp_{K} + 1\)。
经计算得到 \(S = dp_{K} + 1 = 16262\)。
然后把 \(S\) 压进最短路状态里。
得到了复杂度为 \(O(nS \log mS + mS \log K)\) 的做法,相当卡,要剪枝和卡常后才能过,应该不是正解。
代码已经失去了可读性:
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <bitset>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#define pii pair <int , int>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
//const int Mxdt=100000;
//static char buf[Mxdt],*p1=buf,*p2=buf;
//#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++;
template <typename T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
x *= f;
}
template <typename T>
void write(T x , char s='\n') {
if(!x) {putchar('0');putchar(s);return;}
if(x<0) {putchar('-');x=-x;}
T tmp[25]={},t=0;
while(x) tmp[t++]=x%10,x/=10;
while(t-->0) putchar(tmp[t]+'0');
putchar(s);
}
const int MAXN = 3e3 + 5;
int dis[205][16265];
bool tmp[205][16265];
int n , m , k , s , t , head[MAXN] , to[MAXN] , nxt[MAXN] , cnt , l[MAXN] , r[MAXN] , w[MAXN];
void add(int u , int v , int L , int R , int W) {nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;l[cnt] = L;r[cnt] = R;w[cnt] = W;}
int dfn[105] , id[105] , num , dep[105];
void pre(int l , int r , int now) {
if(l == r) return;
dfn[now] = ++num;
id[num] = now;
dep[now] = (1 << (num - 1)) | dep[now >> 1];
int mid = (l + r) >> 1;
pre(l , mid , now << 1) , pre(mid + 1 , r , now << 1 | 1);
}
int tot , np;
int st[16265][30] , siz[16265][30] , ans;
int M[(1 << 25) + 5] , pos[16265];
vector <int> R[30][30];
void find(int l , int r , int now , int x , int y) {
if(l >= x && r <= y) {
R[x][y].push_back(now);
return;
}
int mid = (l + r) >> 1;
if(x <= mid) find(l , mid , now << 1 , x , y);
if(y > mid) find(mid + 1 , r , now << 1 | 1 , x , y);
}
void Dijkstra() {
memset(dis , 127 , sizeof dis);
dis[t][1] = 0;
priority_queue <pair <int , pii> , vector <pair <int , pii> > , greater <pair <int , pii> > > q;
// queue <pair <int , pii> > q;
q.push(mp(0 , mp(t , 1)));
while(!q.empty()) {
int x = q.top().sc.fs , p = q.top().sc.sc;
q.pop();
if(tmp[x][p]) continue;
tmp[x][p] = 1;
if(x == s) break;
if(ans < dis[x][p]) continue;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
np = pos[p];
int val = 0;
for (int j = 0; j < (int)R[l[i]][r[i]].size(); ++j) {
int now = (int)R[l[i]][r[i]][j];
val += w[i] * (siz[p][dfn[now]] + 1);
np |= dep[now >> 1];
}
np = M[np];
if(dis[v][np] > dis[x][p] + val) {
dis[v][np] = dis[x][p] + val;
if(v == s) ans = min(ans , dis[v][np]);
q.push(mp(dis[v][np] , mp(v , np)));
}
}
}
}
int cur[105];
void dfs(int now) {
if(!now) {
int sta = 0;
for (int i = 1; i <= num; ++i) sta |= (cur[i] << (i - 1));
if(!M[sta]) {
M[sta] = ++tot;pos[tot] = sta;
for (int i = 1; i <= num; ++i) st[tot][i] = cur[i];
for (int i = num; i >= 1; --i) siz[tot][i] = st[tot][i] + siz[tot][dfn[id[i] << 1]] + siz[tot][dfn[id[i] << 1 | 1]];
}
return;
}
dfs(now - 1);
if(!cur[now]) {
int t = id[now];
int fc[105] = {};
for (int i = 1; i <= num; ++i) fc[i] = cur[i];
while(t) cur[dfn[t]] = 1 , t >>= 1;
dfs(now - 1);
for (int i = 1; i <= num; ++i) cur[i] = fc[i];
}
}
int main() {
read(n),read(m),read(k),read(s),read(t);
pre(1 , k , 1);
dfs(num);
for (int i = 1; i <= k; ++i) for (int j = i; j <= k; ++j) find(1 , k , 1 , i , j);
for (int i = 1; i <= m; ++i) {
int u , v , l , r , w;
read(u),read(v),read(l),read(r),read(w);
add(v , u , l , r , w);
}
ans = 1e9;
Dijkstra();
write(ans);
return 0;
}