发现Kruskal重构树和倍增通常一起使用呢!
题目描述
自己看题面吧,不可描述.
题目分析
从大体思路入手, 我们可以把从v到1的路径分成两部分, 一半全开车,一半全走路.
也就是说要枚举n个节点作为断点(假设当前断点为u), 这个断点是可行解与最优解当且仅当
存在一条从v到u的路径可以全部开车
且从u到1全部走路的最短路是满足上一条件中最短的
那么要怎么求出从v出发可以开车到的点呢, 显然从v出发开车可以到的点, 一定满足路径上所有边海拔都高于水位,
这里已经很明显可以用Kruskal重构树求解了.
重构树
“查询从某个点出发经过边权不超过val的边所能到达的节点”,诸如此类的题目,第一反应应该就是重构树.
由于本题中有水面高度的限制,不妨以每条边的海拔高度为关键字对边进行由大到小排序,这样的重构树就是一棵小根堆,到时候找大于水面高度的最小值就可以了.
他的子树的全部节点都可以由v开车到达. (由重构树性质得证)
倍增
同样的套路,找水面高度的最小值,用倍增显然快.
以上我们就处理出了大于水面高度的所有点. 那么要到点1的距离最小,是不是就是在这些点里面选呢?所以要处理出点1到大于水面高度的每个点的最短路径,然后再从这些点里面选择到点1距离最小的点.
单源最短路径
显然每个点到1的最短路径是一定的,可以预处理.
不要用! “它死了”,就在这道题!
我们发现,由重构树的性质,能相互通达的点一定有共同的LCA,所以LCA可以维护到点1的路径的最小值,只需在回溯时更新父亲的最小值即可.
程序实现
最后捋一捋思路:我们首先要求出每个点到1号点的最小花费,这个直接最短路预处理. 然后是要建出重构树,再然后维护以每个点作为根节点时子树中距离1号点的最小花费,这个建完树后一个简单的dfs搞定. 最后是如何找到点u,这时我们要让一个重要的算法登场:倍增算法. 直接加上点权>p的限制在树上倍增即可.
听起来很顺?你以为这道题那么简单?不!来看看我这两天碰上的玄学错误吧
#include<bits/stdc++.h>
#define maxn 2000010
using namespace std;
int head[maxn],tot;//用于求最短路
int tree_head[maxn],sum;//用于重构树
int n,m;
int f[maxn],fa[maxn][25],dif[maxn];
struct Edge{
int u,v,len,height,next;
void add(int x,int y,int l,int h){u=x,v=y,len=l,height=h;}
void add_edge(int x,int y,int l,int h){
u=x,v=y,len=l,height=h;
next=head[u];
head[u]=tot;
}
// void _add(int x,int y){
// v=y;
// next=tree_head[u];
// tree_head[u]=sum;
// printf("%d\n",tree_head[u]);
// printf("%d to %d",x,y);
// printf("all right\n");
// }
bool operator <(Edge path)const{
return path.height <height;
}
};
Edge e[maxn];//用于求最短路
Edge E[maxn];//用于构建Kruskal树
Edge ed[maxn];//用于存储输入数据
struct node{
int pos,dis;
bool operator <(node nd)const {
return dis>nd.dis ;
}
};
priority_queue<node >q;
bool vis[maxn];
int dis[maxn];
void dijkstra(){
dis[1]=0;
q.push((node){1,0});
while(!q.empty()){
node now=q.top();
q.pop();
int u=now.pos ;
if(vis[u])continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].next ){
int v=e[i].v ;
if(dis[v]>dis[u]+e[i].len){
dis[v]=dis[u]+e[i].len ;
if(!vis[v])q.push((node){v,dis[v]});
}
}
}
}//预处理最短路
void _add(int x,int y){
E[++sum].v =y;
E[sum].next =tree_head[x];
tree_head[x]=sum;
}
void dfs(int u){
//printf("now is %d\n",u);
for(int i=1;i<=20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
//if(!tree_head[u])return ;
for(int i=tree_head[u];i;i=E[i].next ){
int v=E[i].v ;
dfs(v);
//printf("%d\n",dis[v]);
dis[u]=min(dis[u],dis[v]);
}
return ;
}//dfs维护距离最小值和fa的倍增关系
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int num;
void kruskal(){
for(int i=1;i<=n;i++)f[i]=i;
sort(ed+1,ed+m+1);
int cnt=n;
for(int i=1;i<=m;i++){
int u=ed[i].u ,v=ed[i].v ;
int fu=find(u),fv=find(v);
if(fu==fv)continue;
dif[++cnt]=ed[i].height;
f[fu]=f[fv]=f[cnt]=cnt;
fa[fu][0]=fa[fv][0]=cnt;
// E[++sum]._add(cnt,fu);
// E[++sum]._add(cnt,fv);
_add(cnt,fu);
_add(cnt,fv);//不解释
}
// num=cnt;
// for(int i=1;i<=num;i++)printf("%d ",tree_head[i]);
// printf("\n");
dfs(cnt);
// for(int i=1;i<=cnt;i++)printf("%d ",dis[i]);
// printf("\n");
}
int main(){
int T;
scanf("%d",&T);
for(int up=1;up<=T;up++){
memset(e,0,sizeof e);
memset(ed,0,sizeof ed);
memset(E,0,sizeof E);
memset(vis,0,sizeof vis);
memset(tree_head,0,sizeof tree_head);
memset(head,0,sizeof head);
//f,fa,dif
memset(f,0,sizeof f);
memset(fa,0,sizeof fa);
memset(dif,0,sizeof dif);
scanf("%d%d",&n,&m);
for(int i=1,u,v,l,a;i<=m;i++){
scanf("%d%d%d%d",&u,&v,&l,&a);
ed[i].add(u,v,l,a);
e[++tot].add_edge(u,v,l,a);
e[++tot].add_edge(v,u,l,a);
}
//for(int i=1;i<=100;i++)dis[i]=10000;
memset(dis,0x3f3f3f3f,sizeof dis);
dijkstra();
// dfs(num);
kruskal();
// for(int i=1;i<=num;i++)printf("%d ",dis[i]);
// printf("\n");
int q,k,s,lastans=0;
scanf("%d%d%d",&q,&k,&s);
for(int i=1,v,p;i<=q;i++){
scanf("%d%d",&v,&p);
v=(v+k*lastans-1)%n+1;
p=(p+k*lastans)%(s+1);//在线
for(int i=20;i>=0;i--){
if(fa[v][i]&&dif[fa[v][i]]>p)v=fa[v][i];//倍增找最大的满足条件的点
}
printf("%d\n",dis[v]);
lastans=dis[v];//记得lastans赋值
}}
return 0;
}
upd:我下午又打了一遍,结果更加玄学了。。。这一份76pts
这不完全一样吗!!!!?????
#include<bits/stdc++.h>
#define maxn 2000010
//#define maxn 1600010
//#define maxn 800010
using namespace std;
int tot,head[maxn];
int sum,tree_head[maxn];
int n,m;
struct Edge{
int u,v,len,height,next;
void add(int x,int y,int l,int h){u=x;v=y;len=l;height=h;}
// void add_edge(int x,int y,int l){
// v=y;len=l;next=head[u];
// head[u]=tot;
// }
// void _add(int x,int y){
// v=y;next=tree_head[x];
// tree_head[x]=sum;
// }
bool operator <(Edge path)const {
return path.height <height ;
}
};
Edge ed[maxn];
Edge e[maxn];
Edge E[maxn];
void add_edge(int x,int y,int l){
e[++tot].v =y;
e[tot].len =l;
e[tot].next =head[x];
head[x]=tot;
}
bool vis[maxn];
int dis[maxn];
struct node{
int pos,dis;
bool operator <(node nd)const {
return dis>nd.dis ;
}
};
priority_queue<node>q;
void dijkstra(){
dis[1]=0;
q.push((node){1,0});
while(!q.empty()){
node now=q.top();
q.pop();
int u=now.pos ;
if(vis[u])continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].next ){
int v=e[i].v ;
if(dis[v]>dis[u]+e[i].len ){
dis[v]=dis[u]+e[i].len ;
if(!vis[v]){
q.push((node){v,dis[v]});
}
}
}
}
}
void _add(int x,int y){
E[++sum].v =y;
E[sum].next =tree_head[x];
tree_head[x]=sum;
}
int fa[maxn][25],f[maxn],dif[maxn];
void dfs(int u){
for(int i=1;i<=20;i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
if(!tree_head[u])return ;
for(int i=tree_head[u];i;i=E[i].next ){
int v=E[i].v ;
dfs(v);
dis[u]=min(dis[u],dis[v]);
}
return ;
}
//int num;
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void kruskal(){
for(int i=1;i<=n;i++)f[i]=i;
sort(ed+1,ed+m+1);
int cnt=n;
for(int i=1;i<=m;i++){
int fu=find(ed[i].u );
int fv=find(ed[i].v );
if(fu==fv)continue;
dif[++cnt]=ed[i].height ;
f[fu]=f[fv]=f[cnt]=cnt;
fa[fu][0]=fa[fv][0]=cnt;
// E[++sum]._add(cnt,fu);
// E[++sum]._add(cnt,fv);
_add(cnt,fu);
_add(cnt,fv);
}
dfs(cnt);
// num=cnt;
}
void init(){
memset(head,0,sizeof head);
memset(tree_head,0,sizeof tree_head);
memset(E,0,sizeof E);
memset(ed,0,sizeof ed);
memset(e,0,sizeof e);
memset(vis,0,sizeof vis);
memset(dis,0,sizeof dis);
memset(f,0,sizeof f);
memset(fa,0,sizeof fa);
memset(dif,0,sizeof dif);
}
int main(){
int T;
scanf("%d",&T);
for(int fr=1;fr<=T;fr++){
init();
scanf("%d%d",&n,&m);
for(int i=1,u,v,l,a;i<=m;i++){
scanf("%d%d%d%d",&u,&v,&l,&a);
ed[i].add(u,v,l,a);
// e[++tot].add_edge(u,v,l);
// e[++tot].add_edge(v,u,l);
add_edge(u,v,l);
add_edge(v,u,l);
}
memset(dis,0x3f3f3f3f,sizeof dis);
dijkstra();
kruskal();
// for(int i=1;i<=num;i++)printf("%d ",dis[i]);
// printf("\n");
int q,k,s,lastans=0;
scanf("%d%d%d",&q,&k,&s);
for(int i=1,v,p;i<=q;i++){
scanf("%d%d",&v,&p);
v=(v+k*lastans-1)%n+1;
p=(p+k*lastans)%(s+1);
for(int i=20;i>=0;i--){
if(fa[v][i]&&dif[fa[v][i]]>p)v=fa[v][i];
}
printf("%d\n",dis[v]);
lastans=dis[v];
}
}
return 0;
}
题后总结
我真的连数组大小分析都做不来了么。。。