题目链接
https://www.luogu.org/problemnew/show/P4768
https://www.luogu.org/problemnew/show/U31655
吐槽
垃圾出题人卡什么spfa……而且还是能卡的点全都卡了,用spfa直接60分……
题解
kruskal生成树+倍增+堆优化dijkstra。
首先介绍一下kruskal生成树:
在用kruskal求最小生成树的时候,我们需要先把边排序,每次选长度最小的边,如果连接了两个不同的块,那么选取这条边。
例如下面的这个图:
那么kruskal就将会选取1,2,4三条边。
而kruskal生成树就是对每个点建立一棵树,在kruskal选取一条边的时候,将两个连通块的根的父亲都设为这条边。
例如,上面那个图的kruskal生成树就是:
其中方点代表边,而圆点代表原图中的点。
kruskal生成树的性质:由于从下到上边权越来越大,因此某一条边的子树中边权的最大值是一定的。
回到这道题,如果我们用kruskal对海拔建立一棵最大生成树,那么某一条边在kruskal生成树中子树的海拔最小值就是自己的海拔。因此,从一个点出发能到达的所有点是kruskal生成树的一个子树或一个点。
倍增求出一个点能到达的海拔最低的边,预处理出kruskal生成树中一条边的子树中最小的dist,接下来就很好求了。
时间复杂度O(nlogn+qlogm+mα(n)logm)O(nlogn+qlogm+mα(n)logm)
代码
#include
#include
#include
#include
const int maxn=200000;
const int maxm=400000;
const int inf=0x3f3f3f3f;
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
namespace dsu
{
int fa[maxn*2+10];
int clear()
{
memset(fa,0,sizeof fa);
return 0;
}
int find(int x)
{
return fa[x]?fa[x]=find(fa[x]):x;
}
}
struct edge
{
int u,v,l;
edge(int u_=0,int v_=0,int l_=0)
{
u=u_;
v=v_;
l=l_;
}
bool operator <(const edge &other) const
{
return lint n,m;
namespace tree
{
int pre[maxn*2+10],now[maxn*2+10],son[maxn*2+10],tot;
int val[maxn+10],fa[maxn*2+10][20],dist[maxn*2+10];
int clear()
{
tot=0;
memset(now,0,sizeof now);
memset(val,0,sizeof val);
return 0;
}
int ins(int a,int b)
{
pre[++tot]=now[a];
now[a]=tot;
son[tot]=b;
return 0;
}
int getval()
{
for(int i=1; i<=18; ++i)
{
for(int j=1; j<=2*n+1; ++j)
{
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
return 0;
}
int dfs(int u,int f)
{
fa[u][0]=f;
if(u>=n)
{
return 0;
}
int j=now[u];
dist[u]=inf;
while(j)
{
int v=son[j];
dfs(v,u);
dist[u]=std::min(dist[u],dist[v]);
j=pre[j];
}
return 0;
}
int getdist(int v,int p)
{
v=v+n-1;
if(val[fa[v][0]]<=p)
{
return dist[v];
}
v=fa[v][0];
for(int i=18; i>=0; --i)
{
if(fa[v][i]&&(val[fa[v][i]]>p))
{
v=fa[v][i];
}
}
return dist[v];
}
}
struct data
{
int dist,id;
data(int dist_=0,int id_=0)
{
dist=dist_;
id=id_;
}
bool operator <(const data &other) const
{
return dist>other.dist;
}
};
namespace graph
{
int pre[maxm*2+10],now[maxn+10],son[maxm*2+10],tot;
int dist[maxn+10],val[maxm*2+10],cnt,vis[maxn+10];
edge e[maxm+10];
std::priority_queue h;
int clear()
{
tot=cnt=0;
memset(now,0,sizeof now);
return 0;
}
int ins(int a,int b,int c)
{
pre[++tot]=now[a];
now[a]=tot;
son[tot]=b;
val[tot]=c;
return 0;
}
int add(int a,int b,int c,int d)
{
ins(a,b,c);
ins(b,a,c);
e[++cnt]=edge(a,b,d);
return 0;
}
int kruskal()
{
std::sort(e+1,e+cnt+1);
int need=n-1;
for(int i=cnt; i; --i)
{
int fu=dsu::find(e[i].u+n-1),fv=dsu::find(e[i].v+n-1);
if(fu!=fv)
{
dsu::fa[fu]=dsu::fa[fv]=need;
tree::ins(need,fu);
tree::ins(need,fv);
tree::val[need]=e[i].l;
--need;
if(!need)
{
break;
}
}
}
return 0;
}
int dijkstra(int s)
{
memset(dist,63,sizeof dist);
dist[s]=0;
memset(vis,0,sizeof vis);
h.push(data(0,s));
while(!h.empty())
{
data w=h.top();
h.pop();
if(vis[w.id])
{
continue;
}
vis[w.id]=1;
int j=now[w.id];
while(j)
{
int v=son[j];
if(dist[w.id]+val[j]for(int i=1; i<=n; ++i)
{
tree::dist[i+n-1]=dist[i];
}
return 0;
}
}
int main()
{
int t=read();
while(t--)
{
dsu::clear();
tree::clear();
graph::clear();
n=read();
m=read();
while(m--)
{
int u=read(),v=read(),l=read(),h=read();
graph::add(u,v,l,h);
}
graph::kruskal();
graph::dijkstra(1);
tree::dfs(1,0);
tree::getval();
int q=read(),k=read(),s=read();
int lastans=0;
while(q--)
{
int v=read(),p=read();
v=(v+lastans*k-1)%n+1;
p=(p+lastans*k)%(s+1);
lastans=tree::getdist(v,p);
printf("%d\n",lastans);
}
}
return 0;
}