[NOI2018]归程

题目

思路

做过peaks的话这道题简直就水的不行(\(peaks\)弱化版?)

先从1向所有点跑一次最短路求出\(dis\)(此题略卡\(spfa\)),按照海拔从大到小做出\(kruskal\)重构树,\(dfs\)一遍预处理出每个点对应的子树的最小\(dis\),对于每个海拔限制,倍增找到深度最小的满足条件的节点,则\(ans=mindis\)

两道题的不同点:peaks还要求第k小,要用主席树;这道题求最小值,可以直接预处理(当然还要跑最短路)

Code

(受\(peaks\)影响,蠢的一批的用\(ST+dfs\)序求的子树中的最小\(dis\),这个东西显然可以\(O(1)\)递推的。。。懒得改了)

#include<bits/stdc++.h>
#define N 400005
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int T,n,m,ndsum,fa[N],val[N];
int dis[N],l[N],r[N],rev[N],cc;
int f[N][20],dep[N],st[N][20],lg[N];
bool vis[N];

struct Edge
{
	int next,to,dis;
}edge[N<<2];int head[N],cnt=1;
struct E {int u,v,l,a;} e[N];

void add_edge(int from,int to,int dis)
{
	edge[++cnt].next=head[from];
	edge[cnt].to=to;
	edge[cnt].dis=dis;
	head[from]=cnt;
}
template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}

void init()
{
	memset(dep,0,sizeof(dep));
	memset(head,0,sizeof(head));
	cnt=1;cc=0;
}

bool cmp(E a,E b) {return a.a>b.a;}
int find(int x) {return x==fa[x] ? x : fa[x]=find(fa[x]);}
void dijkstra(int s)
{
	memset(dis,100,sizeof(dis));
	memset(vis,0,sizeof(vis));
	priority_queue< pair<int,int> > q;
	dis[s]=0; q.push(make_pair(-dis[s],s));
	while(!q.empty())
	{
		int u=q.top().second; q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].dis)
			{
				dis[v]=dis[u]+edge[i].dis;
				if(!vis[v]) q.push(make_pair(-dis[v],v));
			}
		}
	}
}
void kruskal()
{
	ndsum=n;
	for(int i=1;i<=n*2;++i) fa[i]=i;
	sort(e+1,e+m+1,cmp);
	int tot=0;
	for(int i=1;i<=m;++i)
	{
		int fx=find(e[i].u),fy=find(e[i].v);
		if(fx==fy) continue;
		val[++ndsum]=e[i].a;
		add_edge(ndsum,fx,0);
		add_edge(ndsum,fy,0);
		fa[fx]=fa[fy]=ndsum;
	}
}
void dfs(int rt)
{
	l[rt]=n+1,r[rt]=0;
	if(rt<=n)
	{
		l[rt]=r[rt]=++cc;
		rev[cc]=rt;
		return;
	}
	for(int i=head[rt];i;i=edge[i].next)
	{
		int v=edge[i].to;
		dep[v]=dep[rt]+1;
		f[v][0]=rt;
		for(int j=1;j<20;++j) f[v][j]=f[f[v][j-1]][j-1];
		dfs(v);
		l[rt]=Min(l[rt],l[v]);
		r[rt]=Max(r[rt],r[v]);
	}
}
int lca(int start,int maxx)
{
	for(int i=19;i>=0;--i)
	  if(f[start][i]&&val[f[start][i]]>maxx)
		start=f[start][i];
	return start;
}
void ST()
{
	memset(st,125,sizeof(st));
	for(int i=n;i>=1;--i)
	{
		st[i][0]=dis[rev[i]];
		for(int j=1;i+(1<<j)-1<=n;++j)
		{
			st[i][j]=Min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
		}
	}
}
int ask(int l,int r)
{
	int tp=lg[r-l+1];
	return Min(st[l][tp],st[r-(1<<tp)+1][tp]);
}

int main()
{
	read(T);
	lg[0]=-1;
	for(int i=1;i<N;++i) lg[i]=lg[i>>1]+1;
	while(T--)
	{
		init();
		read(n);read(m);
		for(int i=1;i<=m;++i)
		{
			read(e[i].u);read(e[i].v);
			read(e[i].l);read(e[i].a);
			add_edge(e[i].u,e[i].v,e[i].l);
			add_edge(e[i].v,e[i].u,e[i].l);
		}
		dijkstra(1);
		memset(head,0,sizeof(head));
		cnt=1;
		kruskal();
		dfs(ndsum);
		ST();
		
		int q,k,s,lasans=0;
		read(q);read(k);read(s);
		while(q--)
		{
			int v,p;
			read(v);read(p);
			v=((long long)v+k*lasans-1)%n+1;
			p=((long long)p+k*lasans)%(s+1);
			int lc=lca(v,p);
			lasans=ask(l[lc],r[lc]);
			printf("%d\n",lasans);
		}
	}
	return 0;
}
posted @ 2019-10-11 07:16  擅长平地摔的艾拉酱  阅读(110)  评论(0编辑  收藏  举报
/*取消选中*/