题意
思考
吐槽一下这个题,完全就是两个裸题拼一起了,而且两个板子之间毫无联系…
首先我们造一个保证字典序最小的最短路径树,怎么保证字典序呢,先将你存的图按字典序从小到大重新排个序再跑最短路就行了。之后就是:跑 (dijkstra),(dfs) 一遍重新建图,如果 (u) 已经被访问过,那么边 (e{u,v,d}) 在最短路上的当且仅当:(dist[u] + d == dist[v]) 。
建完了最短路树之后,问题就是 询问一颗树上的经过点数等于 (k) 的路径长度的最大值,以及最长路径的条数(路径必须满足经过的点数等于 (k))这个就是点分治裸题了。
代码
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x * f;
}
const int N = 30030;
const int M = 60060;
vector<pair<int, int> > G[N];
struct node{
int nxt, to, dis;
}E[M << 1];
int H[N], num2;
void RB(int from, int to, int dis){
E[++num2].nxt = H[from];
E[num2].to = to;
E[num2].dis = dis;
H[from] = num2;
}
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > >q;
int dist[N], vis[N];
void dijkstra(){
memset(vis, 0, sizeof(vis));
memset(dist, 0x3f, sizeof(dist));
dist[1] = 0;
q.push( make_pair(0, 1) );
while(!q.empty()){
int u = q.top().second; q.pop();
if(vis[u]) continue; vis[u] = 1;
for(pair<int, int> V : G[u]){
int v = V.first, d = V.second;
if(dist[v] > dist[u] + d){
dist[v] = dist[u] + d;
q.push( make_pair(dist[v], v) );
}
}
}
}
void rebuild(int u){
vis[u]=1;
for(pair<int, int> V : G[u])
{
int v = V.first, d = V.second;
if(vis[v] || dist[u] + d != dist[v]) continue;
RB(u, v, d); RB(v, u, d); rebuild(v);
}
}
int root, sum, sz[N], f[N], S[N], num[N], ans, ans2, MAXD, n, m, k;
void getroot(int u, int fa){
sz[u] = 1; f[u] = 0;
for(int i=H[u]; i; i=E[i].nxt){
int v = E[i].to;
if(vis[v] || v == fa) continue;
getroot(v, u);
sz[u] += sz[v];
f[u] = max(f[u], sz[v]);
}
f[u] = max(f[u], sum - sz[u]);
if(f[root] > f[u]) root = u;
}
void dfs(int u, int fa, int now){
MAXD = max(MAXD, now);
if(now == k - 1){
if(ans == dist[u]) ans2 ++;
if(dist[u] > ans){
ans2 = 1;
ans = dist[u];
}
return;
}
int nowans = -1;
if(S[k - 1 - now] != -1) nowans = dist[u] + S[k - 1 - now];
if(ans == nowans) ans2 += num[k - 1 - now];
if(nowans > ans){
ans2 = num[k - 1 - now];
ans = nowans;
}
for(int i=H[u]; i; i=E[i].nxt){
int v = E[i].to;
if(vis[v] || v == fa) continue;
dist[v] = dist[u] + E[i].dis;
dfs(v, u, now + 1);
}
}
void update(int u, int fa, int now){
if(now == k - 1) return;
if(S[now] == dist[u]) num[now] ++;
else S[now] = max(S[now], dist[u]), num[now] = 1;
for(int i=H[u]; i; i=E[i].nxt){
int v = E[i].to;
if(vis[v] || v == fa) continue;
update(v, u, now + 1);
}
}
void solve(int u){
MAXD = 0;
vis[u] = 1;
for(int i=H[u]; i; i=E[i].nxt){
int v = E[i].to;
if(vis[v]) continue;
dist[v] = E[i].dis;
dfs(v, u, 1);
update(v, u, 1);
}
for(int i=1; i<=MAXD; i++) S[i] = -1, num[i] = 0;
for(int i=H[u]; i; i=E[i].nxt){
int v = E[i].to;
if(vis[v]) continue;
sum = sz[v]; root = 0;
getroot(v, u);
solve(root);
}
}
int main(){
f[0] = 0x3f3f3f3f;
n = read(), m = read(), k = read();
for(int i=1; i<=m; i++){
int u = read(), v = read(), d = read();
G[u].push_back( make_pair(v, d) );
G[v].push_back( make_pair(u, d) );
}
for(int i=1; i<=n; i++){
sort( G[i].begin(), G[i].end() );
}
dijkstra();
memset(vis, 0, sizeof(vis));
rebuild(1);
sum = n; root = 0;
memset(vis, 0, sizeof(vis));
memset(dist, 0, sizeof(dist));
memset(S, -1, sizeof(S));
getroot(1, 0);
solve(root);
cout << ans << " " << ans2;
return 0;
}
总结
虽说是点分治裸题不过我还是在写的时候犯了一个小错误,就是我之前没有记录 (num[])数组,然后样例一直输出 (3 2),玩了样例之后才发现自己 (sb) 了,所以说以后问题要分析仔细一点…