• 【倍增专题】


    【map】P4889 kls与flag

    可以发现这道题求的是

    assume j>i

    j−i=h[i]+h[j]

    j−i=abs(h[i]=h[j])

    的对数。

    那么显然,因为高度大于0,所以一个数对不可能同时满足两条式子,所以可以分开算

    那么进行分类讨论:

    • (1) j−i=hi+hj -->hi+i=j−hj,那么开一个map,存下所有值的个数,每次都更新答案
    • (2) j−i=hj−hi, -->j−hj=i−hi,同(1)
    • (3) j−i=hi−hj, -->hi+i=hj+j,同(1)

    那么如何保证j>i呢?倒序不就好了吗!

    const int N=2e5+10;
    int h[N];
    int n,m;
    map<int,int>mp;
    #define big long long
    big ans;
    
    int main(){
        rd(n),rd(m);
        rep(i,1,n)rd(h[i]);
        //情况1
        dwn(i,n,1){
            ans+=(big)mp[h[i]+i];
            mp[i-h[i]]++;
        }
        mp.clear();
        //情况3
        dwn(i,n,1){
            ans+=(big)mp[i+h[i]];
            mp[h[i]+i]++;
        }
        mp.clear();
        //情况2
        dwn(i,n,1){
            ans+=(big)mp[i-h[i]];
            mp[i-h[i]]++;
        }
        printf("%lld",ans);
        return 0;
    }
    

    【倍增】P1613 跑路

    先用一个数组预处理出可以一步到达的点对,再上floyd(n<=50不上floyd上什么)

    BTW:部分盆友的智商堪忧,maxlongint 就是maxint 啊,你们开什么64,明明31就行了。​

    const int N=60;
    int n,m;
    int dis[N][N];
    bool G[N][N][64+5];
    
    inline void prework(){
    	rep(logn,1,64)
    		rep(k,1,n)
    			rep(i,1,n)
    				rep(j,1,n)
    					if(G[i][k][logn-1] && G[k][j][logn-1]){
    						G[i][j][logn]=1;
    						dis[i][j]=1;
    					}
    }
    
    inline void floyd(){
    	rep(k,1,n)
    		rep(i,1,n)
    			rep(j,1,n)
    				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    }
    
    int main(){
    	#ifdef WIN32
    	freopen("a.txt","r",stdin);
    	#endif
    	rd(n),rd(m);
    	mem(dis,0x3f);
    	while(m--){
    		int u,v;rd(u),rd(v);
    		G[u][v][0]=1;
    		dis[u][v]=1;
    	}
    	prework();
    	floyd();
    	printf("%d",dis[1][n]);
    	return 0;
    }
    

    【倍增】#137. 关联点

    思考过程:

    每个点网上跳val[i]步,到达的节点就是它产生贡献的节点,可是怎么判断是左边产生的贡献还是右边产生的贡献呢?很简单,我们只需条val[i]-1步,判断当前节点是它父节点的左儿子还是右儿子就好。

    bebug:lson 和rson 不为0的时候才记录啊,我真是个智娃。

    const int N=2e5+10;
    int n,m;
    int val[N],f[N][18+2],son[N][2],ansl[N],ansr[N];
    
    struct edge{
    	int v,next;
    }e[N<<1];
    
    int head[N],edge_num;
    inline void adde(int u,int v){
    	e[++edge_num].v=v;
    	e[edge_num].next=head[u];
    	head[u]=edge_num;
    }
    
    int main(){
    	rd(n);
    	rep(i,1,n){
    		rd(val[i]);val[i]--;//开始就自减一
    	}
    	rep(i,1,n){
    		int lson,rson;
    		rd(lson),rd(rson);
    		if(lson)f[lson][0]=i,son[i][0]=lson;
    		if(rson)f[rson][0]=i,son[i][1]=rson;
    	}
    	rep(i,0,18)
    		rep(u,1,n)
    			f[u][i+1]=f[f[u][i]][i];
    	rep(u,1,n){
    		int x=u;//因为u要一直往上跳,而u还在循环里,不能直接改变,所以多开一个变量
    		dwn(i,18,0)
    			if((1<<i) & val[u])x=f[x][i];//注意这里是&val[u] 而不是一直在跳的val[x]
    		int fa=f[x][0];
    		if(son[fa][0]==x)ansl[fa]++;
    		else ansr[fa]++;
    	}
    	rep(i,1,n)
    		printf("%d %d
    ",ansl[i],ansr[i]);
    	return 0;
    }
    

    【倍增】P1967 货车运输

    SOL

    首先便是想到了Floyd的暴力方法,状态转移方程也不难推出:w[i] [j]=min(w[i] [j], w[i] [k]+w[k] [j]);但是n^ 3次方时间复杂度和n^2的空间复杂度是显然不可取的。

    于是我们思考,可以发现有一些权值较小的边是不会被走过的。正如样例中的第三条边,就算有其他的很多条边,这条边无论如何也是不会被走过的。于是我们想到了可以将图中这样的边去掉,按照这个思路我们便想到了构造最大生成树,将其余的边去除。

    得到了这样一个树之后,我们便考虑如何求出两个节点之间最小边权的最大值(即为题中的最大载重),因为这两点之间的路径是唯一的,我们只需要找出这条路径便可以得到答案。我们可以通过LCA来做到这一点,我求LCA的方法是先从每一个根节点进行搜索,求出节点深度等信息,然后利用这些信息进行树上倍增。

    于是我们可以得出大体思路:首先重新建图,构造出最大生成树,然后在最大生成树上求LCA来回答询问。

    const int N=1e4+10,M=5e4+10;
    int f[N][18],w[N][18],fa[N],deep[N];
    int tt,n,m,q;
    
    struct edge{
    	int u,v,w;
    	bool operator <(const edge &rhs)const{
    		return w>rhs.w;
    	}
    }e[M<<1];
    
    inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    
    struct tree{//最大生成树
    	int v,next,w;
    }tree[N<<1];
    
    int head[N],edge_num;
    inline void adde(int u,int v,int w){
    	tree[++edge_num].v=v;
    	tree[edge_num].w=w;
    	tree[edge_num].next=head[u];
    	head[u]=edge_num;
    }
    
    inline void kruskal(){
    	sort(e+1,e+m+1);
    	rep(i,1,n)fa[i]=i;
    	rep(i,1,m){
    		int fx=find(e[i].u),fy=find(e[i].v);
    		if(fx!=fy){
    			fa[fx]=fy;
    			adde(e[i].u,e[i].v,e[i].w);
    			adde(e[i].v,e[i].u,e[i].w);
    		}
    	}
    }
    
    inline void dfs(int u,int dad,int val){
    	w[u][0]=val;
    	deep[u]=deep[dad]+1;
    	f[u][0]=dad;
    	rep(i,0,tt){
    		f[u][i+1]=f[f[u][i]][i];
    		w[u][i+1]=min(w[u][i],w[f[u][i]][i]);
    	}
    	for(int i=head[u];i;i=tree[i].next){
    		int v=tree[i].v;
    		if(v==dad)continue;
    		dfs(v,u,tree[i].w);
    	}
    }
    
    inline int lca(int u,int v){
    	int res=INT_MAX;
    	if(deep[u]<deep[v])swap(u,v);
    	dwn(i,tt,0){
    		if(deep[u]-(1<<i)>=deep[v]){
    			res=min(res,w[u][i]);
    			u=f[u][i];
    		}
    	}
    	if(u==v)return res;
    	dwn(i,tt,0){
    		if(f[u][i]!=f[v][i]){
    			res=min(res,min(w[u][i],w[v][i]));
    			u=f[u][i],v=f[v][i];
    		}
    	}
    	return min(res,min(w[u][0],w[v][0]));
    }
    
    int main(){
    	mem(w,INT_MAX);
    	rd(n),rd(m);
    	tt=log2(n)+1;
    	rep(i,1,m){
    		rd(e[i].u),rd(e[i].v),rd(e[i].w);
    	}
    	kruskal();
    	rep(i,1,n)//题上没有说是连通图,可能是森林
    		if(i==fa[i])
    			dfs(i,i,INT_MAX);
    	rd(q);
    	while(q--){
    		int u,v;rd(u),rd(v);
    		if(find(u)!=find(v))puts("-1");
    		else printf("%d
    ",lca(u,v));
    	}
        return 0;
    }
    
  • 相关阅读:
    silverlight 跨域文件位置
    Asp.net弹出新窗口,获得返回值
    开通CNblog咯。
    访问IIS元数据库失败
    li可以显示多列
    [转]vs2005 sp1 下载地址、安装方法更新非常慢
    英特尔未来教育核心课程
    很好用的软键盘
    CMD里显示彩色文字
    将输入的中文按要求翻译成拼音
  • 原文地址:https://www.cnblogs.com/sjsjsj-minus-Si/p/11785199.html
Copyright © 2020-2023  润新知