题解 P4768 【[NOI2018]归程】
teafrogsf
2018-07-19 09:10:02
写一下这位dalao@栓犬 告诉我的做法。$Orz$
$Dijkstra$之后~~(出题人:$Spfa$死了)~~,有一个很显然的离线想法是,询问与边都按从大到小排序,然后每次询问倒序进去的时候按比海拔高的约束加入能开车的边,这个东西可以用并查集维护。每次维护这个点子树的最小距离,直接输出就好了。复杂度$O(n\log m+m\log n+q\log n)$。~~然后就有许多同学直接用可持久化并查集直接$O(n\log m+m\log n+q\log ^2n)$暴力艹过去了~~
但实际上这题是可以写到$1$个$\log$的,除了$Kruskal$重构树之外,还可以用$vector$+二分的做法。
我们可以发现,边排序后,答案随着高度变低、并查集合并而逐渐减少,即对于每个点,答案都具有单调递减性。
我们可以开一个$vector$记录每个点在以高度为版本(高度越高,版本越老)的答案,于是并查集合并时直接把小的答案加入父亲的$vector$里。在询问时,找到最近的版本比它老的答案最小的版本,也就是最靠近它且高度比它大的版本。
注意每个点先加入一个版本为$inf$,答案为$dis_i$的答案。
用启发式合并保证复杂度,总时间复杂度为$O(n\log m+m\log n+q\log n)$。
~~还有没有人和我一样考场上没清空$lastans$拿了$65/70pts$的选手啊qwq~~
```cpp
#include<cstdio>
#include<cstring>
#include<queue>
#include<ext/pb_ds/assoc_container.hpp>
#define neko 1000010
#define meko 1000010
#define qeko 1000010
#define fi first
#define se second
#define chkmin(a,b) ((a)<(b)?(a):(b))
#define chkmax(a,b) ((a)>(b)?(a):(b))
#define f(i,a,b) for(register int i=(a);i<=(b);i=-(~(i)))
using namespace std;
typedef long long ll;
typedef pair<ll,int> pi;
struct qwq
{ll ver,ans;};
vector<qwq>vec[neko];
struct node
{int u,v,h,nex;ll w;}e[meko<<1],E[meko];
int t,Et,tp,n,m,Q,K,maxA;
typedef int arr[neko];
arr head,fa,verfa,book,dep;
ll mindis[neko],dis[neko],ans[neko],inf=2e9+1e8,lastans;
template<typename T>
void read(T &x)
{
char c=getchar();x=0;
while(!isdigit(c))c=getchar();
while(isdigit(c))x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
}
namespace Path
{
void add(int x,int y,ll z,int o)
{
e[++t].u=E[++Et].u=x;
e[t].v=E[Et].v=y;
e[t].w=E[Et].w=z;
e[t].h=E[Et].h=o;
e[t].nex=head[x];
head[x]=t;
e[++t].u=y;
e[t].v=x;
e[t].w=z;
e[t].h=o;
e[t].nex=head[y];
head[y]=t;
}
void dijkstra()
{
priority_queue<pi,vector<pi>,greater<pi> >q;
memset(dis,0x3f,sizeof(dis));
dis[1]=0,q.push(pi(0,1));
int u;pi x;
while(!q.empty())
{
x=q.top(),q.pop();
u=x.se;
if(!book[u])
{
book[u]=1;
for(register int i=head[u],v=e[i].v;i;i=e[i].nex,v=e[i].v)
{
if(dis[v]>=x.fi+e[i].w)
{
dis[v]=x.fi+e[i].w;
q.push(pi(dis[v],v));
}
}
}
}
}
}
namespace Uset
{
int find(int x)
{
while(fa[x])x=fa[x];
return x;
}
void insert(int x,ll nowans,ll ver)
{
int len=vec[x].size()-1;
if(nowans<vec[x][len].ans)vec[x].push_back((qwq){ver,nowans});
//printf("xx %lld %lld\n",ver,nowans);
}
void merge(int u,int v,ll w)
{
int x=find(u),y=find(v);
if(x^y)
{
if(dep[x]>dep[y])std::swap(x,y);
fa[x]=y,verfa[x]=w;
dep[y]=chkmax(dep[x]+1,dep[y]);
insert(y,vec[x][vec[x].size()-1].ans,w);
}
}
ll solve(int x,int ver)
{
while(fa[x]&&verfa[x]>ver)x=fa[x];
ll now=2e9+1e8;
int l=0,r=vec[x].size()-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(vec[x][mid].ver>ver)now=chkmin(now,vec[x][mid].ans),l=mid+1;
else r=mid-1;
}return lastans=now;
}
void init()
{memset(dep,0,sizeof(dep)),memset(fa,0,sizeof(fa));}
}
void Init()
{
memset(book,0,sizeof(book));
memset(head,0,sizeof(head));
t=Et=lastans=0;
}
bool cmp2(node a,node b)
{return a.h>b.h;}
int main()
{
using namespace Path;
using namespace Uset;
int x,y,o,cas;ll z;
read(cas);
while(cas--)
{
Init();
read(n),read(m);
f(i,1,m)
{
read(x),read(y),read(z),read(o);
add(x,y,z,o);
}dijkstra(),init();
f(i,1,n)vec[i].clear(),vec[i].push_back((qwq){inf,dis[i]});
sort(E+1,E+Et+1,cmp2);
f(i,1,m)merge(E[i].u,E[i].v,E[i].h);
read(Q),read(K),read(maxA);
f(i,1,Q)
{
read(x),read(y);
x=(x+K*lastans-1)%n+1,y=(y+K*lastans)%(maxA+1);
printf("%lld\n",solve(x,y));
}
//cerr<<clock()<<endl;
}
}
```