[HNOI2016]网络O(n\log n)解法(提高组知识点:线段树上二分+树上两条路径交)

24 篇文章 0 订阅
1 篇文章 0 订阅

题目

题解: 对于每个询问二分答案M,那么转为判断权值大于等于M的路径的交是否包含该点。
建立权值线段树,区间 [ L , R ] [L,R] [L,R]维护权值在 [ L , R ] [L,R] [L,R]范围内的线段的交,可以O(1)合并左右子树的答案
那么二分答案的过程可以改为线段树上二分的过程, O ( log ⁡ n ) O(\log n) O(logn)
插入和删除在没有权值相等的情况下就是简单的把某一个位置赋值或者清零然后向上合并即可。
权值相等时或许可以按照其为第y个出现的权值为x的数,以y为第二关键字排序,最后把排完序后的数组作为离散化后的数组为新的权值来二分,输出的时候对于旧的 l o w e r _ b o u n d lower\_bound lower_bound一下即可。

没有代码。

upd:
我终于拿到了一个LOJ的RK1了!!!!!!2019-10-03 19:19:13
AC   Code: \texttt{AC Code:} AC Code:

#include<bits/stdc++.h>
#define maxn 200005
#define lim 19
#define lc u<<1
#define rc u<<1|1
#define mp make_pair
using namespace std;

char cb[1000000],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1000000,stdin),cs==ct)?0:*cs++)
void read(int &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
char wb[1000000],*wa=wb;

int n,m;
int info[maxn],Prev[maxn],to[maxn],cnt,cnt_e;
pair<int,int>sb[maxn];
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
int dat[maxn][4],st[lim][maxn],lg[maxn],pos[maxn],tot,dep[maxn];

void dfs(int u,int ff){
	dep[u] = dep[ff] + 1;
	st[0][pos[u] = ++tot] = u;
	for(int i=info[u];i;i=Prev[i])
		if(to[i]!=ff){
			dfs(to[i],u);
			st[0][++tot] = u;
		}
}
inline int Lca(int u,int v){
	u = pos[u] , v = pos[v];
	if(u > v) swap(u,v);
	int t = lg[v-u+1];
	return dep[st[t][u]] < dep[st[t][v-(1<<t)+1]] ? st[t][u] : st[t][v-(1<<t)+1];
}

int pt[maxn<<2][2],M;
inline bool cmp(const int &u,const int &v){ return dep[u] > dep[v]; }
inline void merge(int *c,int *a,int *b){
	if(a[0] == -1 || b[0] == -1) c[0] = c[1] = -1;
	else if(!a[0] || !b[0]) c[0] = a[0] + b[0] , c[1] = a[1] + b[1];
	else{
		static int p[4] = {};
		p[0] = Lca(a[0],b[0]) , p[1] = Lca(a[1],b[1]) , p[2] = Lca(a[1],b[0]) , p[3] = Lca(a[0],b[1]);
		sort(p,p+4,cmp);
		if(p[0] != p[1]) c[0] = p[0] , c[1] = p[1];
		else if(p[0] == Lca(a[0],a[1]) || p[0] == Lca(b[0],b[1])) c[0] = c[1] = p[0] = p[1];
		else c[0] = c[1] = -1;
	}
}
inline bool chk(int u,int *p){
	if(p[0] == -1) return 0;
	if(p[0] == 0) return 1;
	if(dep[u] < dep[Lca(p[0],p[1])]) return 0;
	return Lca(u,p[0]) == u || Lca(u,p[1]) == u;
}
inline void Insert(int pos,int p1,int p2){
	pos += M - 1;
	for(pt[pos][0] = p1 , pt[pos][1] = p2;pos>1;pos>>=1)
		merge(pt[pos>>1],pt[pos],pt[pos^1]);
}
inline int solve(int u,int l,int r,int t){
	for(int mid;l<r;){
		mid = (l+r) >> 1;
		if(chk(t,pt[rc])) u=lc,r = mid;
		else u=rc,l = mid+1;
	}
	return l;
}
void put(int a){
	static int q[20]={};
	if(a<0) *wa++ = '-' , a = -a;
	for(;a;a/=10)q[++q[0]]=a%10;
	if(!q[0]) *wa++ = '0';
	for(;q[0];) *wa++ = (q[q[0]--] + '0');
}

int main(){
	read(n),read(m);
	for(int i=1,u,v;i<n;i++){
		read(u),read(v);
		Node(u,v),Node(v,u);
	}
	dfs(1,0);
	for(int j=1;j<lim;j++)
		for(int i=1;i<=tot-(1<<j)+1;i++)
			st[j][i] = (dep[st[j-1][i]] < dep[st[j-1][i+(1<<j-1)]] ? st[j-1][i] : st[j-1][i+(1<<j-1)]);
	for(int i=2;i<=tot;i++)	lg[i] = lg[i>>1] + 1;
	for(int i=1;i<=m;i++){
		read(dat[i][0]),read(dat[i][1]);
		if(dat[i][0] == 0) read(dat[i][2]),read(dat[i][3]),sb[++cnt] = mp(dat[i][3],i);
		if(dat[i][0] == 1) dat[i][3] = dat[dat[i][1]][3];
	}
	sort(sb+1,sb+1+cnt);
	for(M=1;M<cnt;M<<=1);
	for(int i=1;i<=m;i++){
		if(dat[i][0] <= 1) 
			dat[i][3] = lower_bound(sb+1,sb+1+cnt,mp(dat[i][3],dat[i][0] == 0 ? i : dat[i][1])) - sb;
		if(dat[i][0] == 0) Insert(dat[i][3],dat[i][1],dat[i][2]);
		if(dat[i][0] == 1) Insert(dat[i][3],0,0);
		if(dat[i][0] == 2){
			if(chk(dat[i][1],pt[1])) put(-1);
			else put(sb[solve(1,1,M,dat[i][1])].first);
			*wa++ = '\n';
		}
	}
	fwrite(wb,1,wa-wb,stdout);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值