• 倍增法与st


    倍增法

    一篇博客
    最常用,也是最简单的算法,实质就是直接对暴力使用倍增优化将复杂度降低达到需求。有树上的倍增和区间的倍增
    (depth[])为每个节点的深度,(fa[i][j]),i节点的(2^j)的父亲。(lg[i]=log_2{i}+1)

    const int maxn=5000001;
    int depth[maxn],fa[maxn][22],lg[maxn];
    vector<int >g[maxn];
    void dfs(int now,int fath)//dfs初始化fa和depth,传入参数为根节点
    {
    	fa[now][0]=fath;
    	depth[now]=depth[fath]+1;
    	for(int i=1;i<=lg[depth[now]];++i)
    	{
    		fa[now][i]=fa[fa[now][i-1]][i-1];//递推更新fa
    	}
    	for(int i=0;i<g[now].size();i++)//寻找子节点
    	{
    		if(g[now][i]!=fath)
    		dfs(g[now][i],now);
    	}
    }
    int lca(int x,int y)
    {
    	if(depth[x]<depth[y])//使x的深度大于y的深度
    	{
    		swap(x,y);
    	}
    	while(depth[x]>depth[y])
    	{
    		x=fa[x][lg[depth[x]-depth[y]]-1];//使两者深度相同
    	}
    	if(x==y)
    	return x;
    	for(int k=lg[depth[x]]-1;k>=0;--k)//树上倍增
    	{
    		if(fa[x][k]!=fa[y][k])
    		{
    			x=fa[x][k],y=fa[y][k];
    		}
    	}
    	return fa[x][0];
    }
    main(void)
    {
    	int n,m,s;
    	cin>>n>>m>>s;
    	for(int i=1;i<n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		g[x].push_back(y);
    		g[y].push_back(x); 
    	}
    	for(int i=1;i<=n;i++)
    	{
    		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    	}
    	dfs(s,0);
    	for(int i=1;i<=m;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		printf("%d
    ",lca(x,y));	
    	}
    	return 0;
    }
    
    

    题目

    二叉树问题

    统计树的深度、宽度、两个点之间的上行边数和下行边数

    const int maxn=5000001;
    int depth[maxn],fa[maxn][22],lg[maxn];
    vector<int >g[maxn];
    int ans1=0;
    int ans2[20000]={0};
    int ans22=0;
    void dfs(int now,int fath)
    {
    	fa[now][0]=fath;
    	depth[now]=depth[fath]+1;
    	ans1=max(ans1,depth[now]);
    	ans2[depth[now]]++;
    	ans22=max(ans22,ans2[depth[now]]);
    	for(int i=1;i<=lg[depth[now]];++i)
    	{
    		fa[now][i]=fa[fa[now][i-1]][i-1];
    	}
    	for(int i=0;i<g[now].size();i++)
    	{
    		if(g[now][i]!=fath)
    		dfs(g[now][i],now);
    	}
    }
    int lca(int x,int y)
    {
    	
    	int ans=0;
    	int ans11=0;
    	int flag=0;
    	if(depth[x]<depth[y])
    	{
    		swap(x,y);
    		flag=1;
    	}
    	while(depth[x]>depth[y])
    	{
    		ans11+=1<<(lg[depth[x]-depth[y]]-1);
    		x=fa[x][lg[depth[x]-depth[y]]-1];
    	}
    	if(x==y)
    	{
    		if(flag)
    		return ans11;
    		return 2*ans11;
    	}
    	for(int k=lg[depth[x]]-1;k>=0;--k)
    	{
    		if(fa[x][k]!=fa[y][k])
    		{
    			x=fa[x][k],y=fa[y][k];
    			ans+=1<<k;
    			ans11+=1<<k;
    		}
    	}
    	if(flag)
    	return (ans+1)*2+(ans11+1);
    	return (ans+1)+(ans11+1)*2;
    }
    main(void)
    {
    	int n,m,s=1,u,v;
    	cin>>n;
    	for(int i=1;i<n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		g[x].push_back(y);
    		g[y].push_back(x); 
    	}
    	for(int i=1;i<=n;i++)
    	{
    		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    	}
    	dfs(s,0);
    	cin>>u>>v;
    	printf("%d
    %d
    %d
    ",ans1,ans22,lca(u,v));
    	return 0;
    }
    
    

    P2420 让我们异或吧

    运用lca中的dfs,求得所有点到根的异或

    const int maxn=5000001;
    int depth[maxn],fa[maxn][22],lg[maxn];
    vector<int >g[maxn];
    int ans1=0;
    int ans2[20000]={0};
    int ans22=0;
    void dfs(int now,int fath)
    {
    	fa[now][0]=fath;
    	depth[now]=depth[fath]+1;
    	ans1=max(ans1,depth[now]);
    	ans2[depth[now]]++;
    	ans22=max(ans22,ans2[depth[now]]);
    	for(int i=1;i<=lg[depth[now]];++i)
    	{
    		fa[now][i]=fa[fa[now][i-1]][i-1];
    	}
    	for(int i=0;i<g[now].size();i++)
    	{
    		if(g[now][i]!=fath)
    		dfs(g[now][i],now);
    	}
    }
    int lca(int x,int y)
    {
    	
    	int ans=0;
    	int ans11=0;
    	int flag=0;
    	if(depth[x]<depth[y])
    	{
    		swap(x,y);
    		flag=1;
    	}
    	while(depth[x]>depth[y])
    	{
    		ans11+=1<<(lg[depth[x]-depth[y]]-1);
    		x=fa[x][lg[depth[x]-depth[y]]-1];
    	}
    	if(x==y)
    	{
    		if(flag)
    		return ans11;
    		return 2*ans11;
    	}
    	for(int k=lg[depth[x]]-1;k>=0;--k)
    	{
    		if(fa[x][k]!=fa[y][k])
    		{
    			x=fa[x][k],y=fa[y][k];
    			ans+=1<<k;
    			ans11+=1<<k;
    		}
    	}
    	if(flag)
    	return (ans+1)*2+(ans11+1);
    	return (ans+1)+(ans11+1)*2;
    }
    main(void)
    {
    	int n,m,s=1,u,v;
    	cin>>n;
    	for(int i=1;i<n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		g[x].push_back(y);
    		g[y].push_back(x); 
    	}
    	for(int i=1;i<=n;i++)
    	{
    		lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    	}
    	dfs(s,0);
    	cin>>u>>v;
    	printf("%d
    %d
    %d
    ",ans1,ans22,lca(u,v));
    	return 0;
    }
    
    

    疫情控制

    要求分配军队,运用了lca的dfs,得到每个点的祖先节点情况。

    const int N=5e4+3;
    ll sum,dis[N],dist[N];
    int n,m,u,cnt,cont;
    int am[N],lg[N],f[N][17],dp[N],vis[N],fre[N],b[N],pl[N],nee[N];
    int tt,head[N],to[N<<1],nex[N<<1],w[N<<1];
    vector<int>arv[N];
    struct LCA{//直接套的lca模板
    	void pre(int g,int F){//预处理父亲;
    		for(int i=head[g],v;i;i=nex[i]){
    			v=to[i];
    			if(v==F)continue;
    			f[v][0]=g,dp[v]=dp[g]+1,dis[v]=dis[g]+(ll)w[i];
    			for(int j=1;j<=lg[dp[v]];++j)
    				f[v][j]=f[f[v][j-1]][j-1];
    			pre(v,g);
    		}
    		return ;
    	}
    	int get(int x,int y){
    		if(dp[x]>dp[y])swap(x,y);
    		for(int i=lg[dp[y]-dp[x]];i>=0;--i)
    			if(dp[f[y][i]]>=dp[x])y=f[y][i];
    		if(x==y)return x;
    		for(int i=lg[dp[x]];i>=0;--i)
    			if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    		return f[x][0];
    	}
    }lca;
    inline void add(int x,int y,int W){
    	to[++tt]=y,nex[tt]=head[x],w[tt]=W,head[x]=tt;
    	return ;
    }
    inline bool dfs(int g,int F){
    	if(vis[g]&&!pl[g])return true;//若当前节点不是根节点的子节点,且有标记,说明当前子树已全部被标记;
    	bool flag=0;
    	for(int i=head[g],v;i;i=nex[i]){
    		v=to[i];
    		if(v==F)continue;
    		if(!dfs(v,g))return false;//有一个叶子节点未被标记就需要加入need集合;
    		flag=1;
    	}
    	return flag;
    }
    inline bool check(ll mid){
    	for(int i=1;i<=cnt;++i)arv[i].clear();
    	memset(vis,0,sizeof vis);
    	for(int i=1;i<=m;++i){
    		int y=am[i];
    		dist[i]=0;
    		for(int j=dp[y];j>=0;--j)//;将其向父亲跳
    			if(f[y][j]>1&&dist[i]+dis[y]-dis[f[y][j]]<=mid){
    				dist[i]+=dis[y]-dis[f[y][j]];
    				y=f[y][j];
    			}
    		vis[y]=1;//将当前节点标记;
    		int j=pl[y];//找到对应的根节点的子节点的编号;
    		if(j){
    			arv[j].push_back(mid-dist[i]);//将剩余距离加入第j个子节点;
    			if(arv[j].size()>1&&arv[j][arv[j].size()-2]<arv[j][arv[j].size()-1])
    				swap(arv[j][arv[j].size()-2],arv[j][arv[j].size()-1]);//将剩余距离最短的点放到尾部;
    		}
    	}
    	int cnt1=0,cnt2=0;
    	for(int i=1;i<=cnt;++i){
    		if(!dfs(to[b[i]],1)){//判定是否需要军队驻扎;
    			if(arv[i].size()&&arv[i][arv[i].size()-1]<w[b[i]]*2)
    				arv[i].pop_back();//若当前点存在无法先到达根节点,再返回当前点的的军队,就将其军队用来驻扎当前点;
    			else nee[++cnt1]=w[b[i]];//否则将当前点加入need集合;
    		}
    		for(int j=0;j<arv[i].size();++j)
    			if(arv[i][j]>=w[b[i]])
    				fre[++cnt2]=arv[i][j]-w[b[i]];//将符合条件的军队加入free集合;
    	}
    	if(cnt1>cnt2)return false;//如果军队数小于节点数,就无法完全覆盖;
    	sort(nee+1,nee+1+cnt1);
    	sort(fre+1,fre+1+cnt2);//排序;
    	while(cnt1){//反向比较;
    		if(fre[cnt2]<nee[cnt1])return false;//若当前点无法被当前军队覆盖,那么在他之后的军队也无法将其覆盖;
    		--cnt2,--cnt1;
    	}
    	return true;//将节点完全覆盖;
    }
    inline ll solve(){
    	ll l=0,r=sum+1;
    	while(l<r){
    		ll mid=l+r>>1;
    		if(check(mid))r=mid;
    		else l=mid+1;
    	}
    	return l;
    }
    int main(){
    	cin>>n;
    	lg[0]=-1;
    	for(int i=1;i<=n;++i)lg[i]=lg[i>>1]+1;//初始化lg数组;
    	int x,y,W;
    	for(int i=1;i<n;++i)cin>>x>>y>>W,add(x,y,W),add(y,x,W),sum+=W;
    	for(int i=head[1],v;i;i=nex[i])v=to[i],b[++cnt]=i,pl[v]=cnt;//b记录根节点的第cnt条边,pl记录节点对应第几条边;
    	cin>>m;
    	lca.pre(1,0);
    	for(int i=1;i<=m;++i)cin>>x,am[i]=x;
    	if(m<cnt)puts("-1");//若军队数小于根节点的子节点数,则一定无法完全覆盖;
    	else cout<<solve()<<'
    ';//否则一定可以完全覆盖;
    	return 0;
    }
    
    

    P3865 【模板】ST表

    求区间最值
    传送门

    const int maxn=100005;
    int a,n;
    int f[maxn][22];
    inline int read()
    {
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    struct ST
    {
    	void init()
    	{
    		_1for(i,n)
    		{
    			scanf("%d",&f[i][0]);
    		}
    		for(int j=1;j<=21;j++)
    		{
    			for(int i=1;i+(1<<j)-1<=n;i++)
    			{
    				f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    			}
    		}
    	}
    	int check(int l,int r)
    	{
    		int k=log2(r-l+1);	
    		return max(f[l][k],f[r-(1<<k)+1][k]);
    	}
    };
    main(void)
    {
    	int m;
    	n=read();
    	m=read();
    	ST P;
    	P.init();
    	_1for(i,m)
    	{
    		int l,r;
    		l=read();
    		r=read();
    		printf("%d
    ",P.check(l,r));
    	}
    }
    
    
  • 相关阅读:
    使用内部单向链表实现的一个简单堆栈
    通过反射调用内部类的隐藏方法
    动态代理生成空对象
    通过使用java.lang.reflect.Proxy实现动态代理
    简单代理模式
    暗色CSS,适用与Stylish, IE, FF, OPERA等.
    CWnd派生的控件处理MouseMove, MouseHover, MouseLeave
    _tcscpy_s的size应至少为src的长度+1(要把计算在内)
    用INET(CHttpFile)下载有重定向链接时获取最终URL的方法.
    GetDlgItem以及其他获得CWnd相关的函数要注意。。
  • 原文地址:https://www.cnblogs.com/wangqianyv/p/13284964.html
Copyright © 2020-2023  润新知