ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NOIP2016&洛谷P1600:天天爱跑步

2021-11-01 23:03:25  阅读:136  来源: 互联网

标签:NOIP2016 洛谷 int top ++ dep lca P1600 return


文章目录

解析

个人认为本题比同年的逛公园可做许多

本题的一个关键是:把慢跑者 ( u , v ) (u,v) (u,v)转化为上升路径上满足 d e p x + t x = d e p u dep_x+t_x=dep_u depx​+tx​=depu​的结点和下降路径上满足 − d e p + x + t x = d e p u − 2 ∗ d e p l c a -dep+x+t_x=dep_u-2*dep_{lca} −dep+x+tx​=depu​−2∗deplca​的结点x的答案均加一

这个感觉不是很难想,由于每秒跑一步的特性,很容易想到这个和深度的联系

然后就是怎么维护上面那个玩意了

sol1:树剖+map

时间复杂度:O(nlogn^3)
得分:95pts
这是我一开始想到的解法
第一交95pts也可以接受了吧
极其好写
就是对线段树每个结点开个map,做一个标记永久化,然后询问的时候沿途把标记捡起来就行了
修改过程中只会在最终打标记处改变map
一开始我觉得这个改变次数是O(1)的,但是这个是**假的!!**在最差情况下会退化成Ologn个(比如修改[1,n-1]的区间时)
这样复杂度就升到了nlogn^3,无法通过了
T掉3e5的数据也是合情合理
由于树剖自带的小常数+氧气加持+上面那个第三个log完全跑不满(现在终于可以默认有O2了!),得到95分也算合理
(然而关掉氧气由于垃圾的map常数就只有25pts)

sol2:树剖+离线

时间复杂度:nlogn^2
得分:100pts
稍微思考一下就可以想到真的做法
注意到所有的操作和询问都是针对一个特定的值,把他们离线下来从大到小处理即可
询问的时候用到一个先减后加从而求出变化量
这个就跑的飞快了
也不再耗氧
### 代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define debug(a,b) fprintf(stderr,a,b)
const int N=3e5+100;
const double eps=1e-9;
inline ll read() {
	ll x=0,f=1;
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) {
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	return x*f;
}


int n,m;
struct node {
	int to,nxt;
} p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y) {
	p[++cnt]=(node) {
		y,fi[x]
	};
	fi[x]=cnt;
	return;
}
int fa[N],siz[N],dep[N],hson[N],top[N],dfn[N],pos[N],tim;
void dfs1(int x,int f) {
	fa[x]=f;dep[x]=dep[f]+1;
	siz[x]=1;
	for(int i=fi[x]; ~i; i=p[i].nxt) {
		int to=p[i].to;
		if(to==f) continue;
		dfs1(to,x);
		siz[x]+=siz[to];
		if(siz[to]>siz[hson[x]]) hson[x]=to;
	}
	return;
}
void dfs2(int x,int tp) {
	top[x]=tp;
	dfn[++tim]=x;pos[x]=tim;
	if(hson[x]) dfs2(hson[x],tp);
	for(int i=fi[x]; ~i; i=p[i].nxt) {
		int to=p[i].to;
		if(to==hson[x]||to==fa[x]) continue;
		dfs2(to,to);
	}
	return;
}
inline int Lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	} 
	if(dep[x]<dep[y]) swap(x,y);
	return y;
}

#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)

int ans[N];

struct tree{
	int val[N<<2];
	int ask(int k,int l,int r,int x){
		int res=val[k];
		if(l==r) return res;
		if(x<=mid) return ask(ls,l,mid,x)+res;
		else return ask(rs,mid+1,r,x)+res;
	}
	void change(int k,int l,int r,int x,int y){
		if(x>y) return;
		//printf("k=%d (%d %d) x=%d y=%d v=%d\n",k,l,r,x,y,v);
		if(x<=l&&r<=y){
			val[k]++;
			return;
		}
		if(x<=mid) change(ls,l,mid,x,y);
		if(y>mid) change(rs,mid+1,r,x,y);
		return;
	}
	inline void Add(int x,int anc,int flag){
		while(top[x]!=top[anc]){
			change(1,1,n,pos[top[x]],pos[x]);
			x=fa[top[x]];
		}
		change(1,1,n,pos[anc]+flag,pos[x]);
		return;
	}
}t[2];

int tt[N];

int tot1,tot2;
struct ope{
	int x,anc,val,op;
	bool operator < (const ope u)const{return val<u.val;}
}o[N<<1];
struct query{
	int x,val,op;
	bool operator < (const query u)const{return val<u.val;}
}q[N<<1];
int main() {
#ifndef ONLINE_JUDGE
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
#endif
	memset(fi,-1,sizeof(fi));cnt=-1;
	n=read();
	m=read();
	for(int i=1; i<n; i++) {
		int x=read(),y=read();
		addline(x,y);
		addline(y,x);
	}
	
	dfs1(1,0);
	dfs2(1,1);
	
	for(int i=1;i<=n;i++){
		tt[i]=read();
		q[++tot1]=(query){i,tt[i]+dep[i],0};
		q[++tot1]=(query){i,tt[i]-dep[i],1};
	}
	//for(int i=1;i<=n;i++) printf("x=%d fa=%d dep=%d t=%d pos=%d v1=%d v2=%d\n",i,fa[i],dep[i],t[i],pos[i],t[i]+dep[i],t[i]-dep[i]);
	
	
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		int lca=Lca(x,y);
		o[++tot2]=(ope){x,lca,dep[x],0};
		o[++tot2]=(ope){y,lca,dep[x]-2*dep[lca],1};
		//t1.Add(x,lca,dep[x],1);
		//t2.Add(y,lca,dep[x]-2*dep[lca],0);
		//printf("x=%d y=%d lca=%d v1=%d v2=%d\n",x,y,lca,dep[x],dep[x]-2*dep[lca]);
	}
	
	sort(q+1,q+1+tot1);sort(o+1,o+1+tot2);
	int pl1=1,pl2=1;
	for(int tim=-2*n;tim<=2*n;tim++){
		for(int i=pl1;i<=tot1&&q[i].val==tim;i++){
			int now=q[i].x;
			ans[now]-=t[q[i].op].ask(1,1,n,pos[now]);
		}
		while(pl2<=tot2&&o[pl2].val==tim){
			t[o[pl2].op].Add(o[pl2].x,o[pl2].anc,o[pl2].op);
			++pl2;
		}
		while(pl1<=tot1&&q[pl1].val==tim){
			int now=q[pl1].x;
			ans[now]+=t[q[pl1].op].ask(1,1,n,pos[now]);
			++pl1;
		}
	}
	
	for(int i=1;i<=n;i++){
		printf("%d ",ans[i]);
	}
	return 0;
}
/*
6 1
2 3
1 2 
1 4 
4 5 
4 6 
0 2 5 1 2 3
1 5
*/

sol3:dfs维护树状数组+差分

时间复杂度:nlogn
得分:100pts
写完看的题解的思路
这个东西是真的好强大
有点四两拨千斤的感觉
以后可以多往这方面想一想
就是把我上面维护的东西离线到各个结点上
维护树状数组并利用前后差值求出答案
(应该)跑的飞快
说是应该是因为我并没有再写一遍

标签:NOIP2016,洛谷,int,top,++,dep,lca,P1600,return
来源: https://blog.csdn.net/BUG_Creater_jie/article/details/121090527

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有