zoukankan      html  css  js  c++  java
  • 股神小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;
    }
  • 相关阅读:
    优化SQL查询:如何写出高性能SQL语句
    提高SQL执行效率的16种方法
    Spring Ioc DI 原理
    java内存泄漏
    转:js闭包
    LeetCode Best Time to Buy and Sell Stock III
    LeetCode Best Time to Buy and Sell Stock with Cooldown
    LeetCode Length of Longest Fibonacci Subsequence
    LeetCode Divisor Game
    LeetCode Sum of Even Numbers After Queries
  • 原文地址:https://www.cnblogs.com/OYJason/p/9693150.html
Copyright © 2011-2022 走看看