• 股神小D


    题目大意:

    给定一棵树,每一条边有$L,R$两种权值,求有多少条路径满足$max(L)leqmin(R)$。

    解法$1-$点分治$+$二维数点

    统计树上的路径应首先想到点分治,我们很显然可以搜出过从分治重心出发的每一条路径,对应着当前重心的每一棵子树存在的若干个区间$[L_i,R_i]$,若两个不同的子树内的区间产生贡献,即这两个点形成的路径符合要求,当且仅当$[L_1,R_1]cap[L_2,R_2] eemptyset$,换言之,当两个点形成的路径没有贡献,当且仅当$R_1<L_2$或$R_2<L_1$,那么考虑先离散化,按顺序处理每一棵子树时,用树状数组维护已插入的点中$R$值和$L$值。这样,枚举到当子树时,用之前枚举的所子树中点的数量,减去$L$值过大和$R$值过小的数量,就得到了这个点对最终答案的影响。

    每一条符合路径的路径仅会被路径上等级最高的分治重心所统计。如果最高等级的分治重心有两个,那么他们之间一定会有一个等级更高的分治重心。

    最终复杂度为$O(Nlog^2N)$,所以本机跑了$1.7s$考场上拿到了$91$分$hhh$。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 200004
    using namespace std;
    int read(){
    	int nm=0,fh=1; char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    int n,m,c[M<<2][2],u[M],v[M],L[M<<1],R[M<<1],G[M],cnt;
    int tmp,fs[M],nt[M<<1],to[M<<1],U[M<<1],D[M<<1];
    int id[M<<1],od[M<<1],sum,rt,sz[M],mxs[M];
    void add(int k,int x,int kd){for(;k<=m;k+=(k&-k)) c[k][kd]+=x;}
    int qry(int k,int kd){int tt=0;for(;k;k-=(k&-k)) tt+=c[k][kd]; return tt;}
    LL ans;
    bool cmp(int x,int y){return (od[x]+od[y]==0)?od[x]<od[y]:abs(od[x])<abs(od[y]);}
    bool vis[M];
    void fdrt(int x,int last){
    	sz[x]=1,mxs[x]=0;
    	for(int i=fs[x];~i;i=nt[i]){
    		if(to[i]==last||vis[to[i]]) continue;
    		fdrt(to[i],x),sz[x]+=sz[to[i]];
    		mxs[x]=max(mxs[x],sz[to[i]]);
    	}
    	mxs[x]=max(mxs[x],sum-sz[x]);
    	if(mxs[x]<mxs[rt]) rt=x;
    }
    void dfs(int x,int last,int minn,int maxn){
    	if(minn>maxn) return; sz[x]=1;
    	ans+=cnt-qry(m-maxn,0)-qry(minn-1,1);
    	for(int i=fs[x];i!=-1;i=nt[i]){
    		if(vis[to[i]]||to[i]==last) continue;
    		dfs(to[i],x,max(minn,L[i]),min(maxn,R[i]));
    		sz[x]+=sz[to[i]];
    	}
    }
    void ins(int x,int last,int minn,int maxn){
    	if(minn>maxn) return; cnt++;
    	add(m-minn+1,1,0),add(maxn,1,1);
    	for(int i=fs[x];i!=-1;i=nt[i]){
    		if(vis[to[i]]||to[i]==last) continue;
    		ins(to[i],x,max(minn,L[i]),min(maxn,R[i]));
    	}
    }
    void del(int x,int last,int minn,int maxn){
    	if(minn>maxn) return;
    	add(m-minn+1,-1,0),add(maxn,-1,1);
    	for(int i=fs[x];i!=-1;i=nt[i]){
    		if(vis[to[i]]||to[i]==last) continue;
    		del(to[i],x,max(minn,L[i]),min(maxn,R[i]));
    	}
    }
    void solve(int x){
    	vis[x]=true,cnt=1;
    	for(int i=fs[x];i!=-1;i=nt[i]) if(!vis[to[i]]) dfs(to[i],x,L[i],R[i]),ins(to[i],x,L[i],R[i]);
    	for(int i=fs[x];i!=-1;i=nt[i]) if(!vis[to[i]]) del(to[i],x,L[i],R[i]);
    	for(int i=fs[x];i!=-1;i=nt[i]) if(!vis[to[i]]) sum=sz[to[i]],rt=0,fdrt(to[i],x),solve(rt);
    }
    int main(){
    	n=read(),sum=n,m=(n<<1)-2,memset(fs,-1,sizeof(fs)),mxs[0]=n+1;
    	for(int i=1;i<n;i++){
    	    u[i]=read(),v[i]=read(),D[i]=read(),U[i]=read();
    	    id[(i<<1)-1]=(i<<1)-1,id[i<<1]=(i<<1);
    	    od[(i<<1)-1]=-D[i],od[i<<1]=U[i];
    	}
    	sort(id+1,id+m+1,cmp);
    	for(int i=1;i<=(n<<1);i++){
    		if(id[i]&1) D[(id[i]+1)>>1]=i;
    		else U[id[i]>>1]=i;
    	}
    	for(int i=1;i<n;++i){
    		nt[tmp]=fs[u[i]],fs[u[i]]=tmp,to[tmp]=v[i],L[tmp]=D[i],R[tmp++]=U[i];
    		nt[tmp]=fs[v[i]],fs[v[i]]=tmp,to[tmp]=u[i],L[tmp]=D[i],R[tmp++]=U[i];
    	}
    	fdrt(1,0),solve(rt),printf("%lld
    ",ans); return 0;
    }
    

    解法$2-$动态树

    动态树的解法非常巧妙,把每一条$(u,v,L,R)$的边看做一次$(u,v,L)$的$Link$和$(u,v,R)$的$Cut$,这样,我们先把每一条边拆成两条,按照权值对操作排序,贡献值和恰好就是每次$Link(u,v)$时$u,v$所在的两个连通块点数的乘积,每条合法的路径一定会被路径上的$max(L)$的$Link$操作所统计。

    复杂度为常规$LCT$的$O(Nlog N)$

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 200020
    #define ls c[x][0]
    #define rs c[x][1]
    using namespace std;
    int read(){
    	int nm=0,fh=1; char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    LL ans;
    int n,m,fa[M],c[M][2],sz[M],tk[M],rev[M],vm[M],S[M],top;
    void pushdown(int x){if(rev[x]) swap(ls,rs),rev[ls]^=1,rev[rs]^=1,rev[x]=0;}
    void pushup(int x){sz[x]=sz[ls]+sz[rs]+vm[x]+1;}
    bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
    void rotate(int x){
    	int tp=fa[x],dtp=fa[fa[x]],ms,ds;
    	if(!isroot(tp)){if(c[dtp][0]==tp) c[dtp][0]=x;else c[dtp][1]=x;}
    	if(c[tp][0]==x) ds=1,ms=0; else ds=0,ms=1;
    	fa[x]=dtp,fa[tp]=x,fa[c[x][ds]]=tp;
    	c[tp][ms]=c[x][ds],c[x][ds]=tp,pushup(tp),pushup(x);
    }
    void splay(int x){
    	S[top=1]=x;
    	for(int y=x;!isroot(y);y=fa[y]) S[++top]=fa[y];
    	while(top) pushdown(S[top]),top--;
    	while(!isroot(x)){
    		int tp=fa[x];
    		if(isroot(tp)) return rotate(x);
    		else if(c[c[fa[tp]][0]][0]==x) rotate(tp);
    		else if(c[c[fa[tp]][1]][1]==x) rotate(tp);
    		else rotate(x);
    	}
    }
    void access(int x){for(int y=0;x;y=x,x=fa[x]) splay(x),vm[x]+=sz[rs]-sz[y],rs=y,pushup(x);}
    void chroot(int x){access(x),splay(x),rev[x]^=1;}
    void cut(int x,int y){chroot(y),access(x),splay(x),ls=fa[y]=0,pushup(x);}
    void link(int x,int y){chroot(x),chroot(y),ans+=(LL)sz[x]*(LL)sz[y],fa[x]=y,vm[y]+=sz[x],pushup(y);}
    struct opt{
    	int Typ,X,Y,G; opt(){}
    	opt(int _Typ,int _X,int _Y,int _G){Typ=_Typ,X=_X,Y=_Y,G=_G;}
    	void ect(){if(Typ==0) link(X,Y); else cut(X,Y);}
    }q[M<<1];
    bool cmp(opt i,opt j){return i.G==j.G?i.Typ<j.Typ:i.G<j.G;}
    int main(){
    	n=read();
    	for(int i=1;i<n;i++){
    		int u=read(),v=read(),D=read(),U=read();
    		m++,q[m]=opt(0,u,v,D),m++,q[m]=opt(1,u,v,U);
    	} sort(q+1,q+m+1,cmp);
    	for(int i=1;i<=n;i++) sz[i]=1;
    	for(int i=1;i<=m;i++) q[i].ect();
    	printf("%lld
    ",ans);return 0;
    }
  • 相关阅读:
    java中Condition类的详细介绍(详解)
    Semaphore原理以及使用总结
    RocketMQ消息存储原理总结(一)
    Es Bucket聚合(桶聚合)总结
    "元素隐式具有 “any” 类型,因为类型“Shared”没有索引签名"问题解决思路
    有关生产环境tomcat java应用报错Caused by: java.lang.OutOfMemoryError: Java heap space的调研
    docker 实践02
    正睿比赛总结day1
    【水】一个神秘玩意:如何分辨出差别很小的颜色?
    AtCoder Regular Contest 125 比赛记录(vp)
  • 原文地址:https://www.cnblogs.com/OYJason/p/9693150.html
Copyright © 2020-2023  润新知