• 【CF1270H】Number of Components


    题目

    题目链接:https://codeforces.com/problemset/problem/1270/H
    给一个长度为 \(n\) 的数组 \(a\)\(a\) 中的元素两两不同。
    对于每个数对 \((i,j)(i<j)\),若 \(a_i<a_j\),则让 \(i\)\(j\) 连一条边。求图中连通块个数。
    支持 \(q\) 次修改数组某个位置的值,每次修改后输出图中连通块个数。
    \(n,q\le 5\times 10^5,1\le a_i\le 10^6\),保证任意时刻数组中元素两两不同。

    思路

    有一个性质:一个连通块一定是序列里的一个区间。
    \(i<j\)\(k\in(i,j)\),且 \(i,j\) 在一个连通块内:

    • 如果 \(a_i<a_j\),那么显然至少满足 \(a_i<a_k\)\(a_j>a_k\) 中的一个。
    • 如果 \(a_i>a_j\),必然存在 \(x<i\)\(a_x<a_j<a_i\) 或者 \(y>j\)\(a_y>a_i>a_j\),拿前者举例,\([x,i]\)\([x,j]\) 都必然在同一个连通块内,\([i,j]\) 也就在同一个连通块内。

    那么问题就转化为有多少个位置 \(p\) 满足 \(\min(a_1\cdots a_{p-1})>\max(a_p\cdots a_n)\)
    考虑枚举 \(v=\max(a_p\cdots a_n)\),可以把 \(>v\) 的设为 \(1\)\(\leq v\) 的设为 \(0\),那么这个 \(v\) 能分割两个连通块当且仅当这个 \(01\) 序列单调不增。
    \(a_0=+\infty,a_{n+1}=0\),那么对于任意的 \(v\),其 \(01\) 序列至少包含一个 \(0\) 和一个 \(1\),那么问题又可以转化为求有多少个 \(v\) 满足其对应的 \(01\) 序列只存在一组 \(0,1\) 相邻。
    对值域建立一棵线段树,对于相邻的元素 \(a_i,a_{i+1}\),他们能对 \([\min(a_i,a_{i+1}),\max(a_i,a_{i+1}))\) 里面的 \(v\) 贡献一组相邻的 \(0,1\)。那么就将这个区间加 \(1\)。每次询问只需要查询有多少个在序列里的数字线段树上只有 \(1\) 点贡献即可。
    时间复杂度 \(O((Q+n)\log V)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1000010,lim=1e6+1;
    int n,Q,a[N];
    
    struct SegTree
    {
    	int cnt[N*4],minv[N*4],lazy[N*4];
    	
    	void pushdown(int x)
    	{
    		if (!lazy[x]) return;
    		minv[x*2]+=lazy[x]; minv[x*2+1]+=lazy[x];
    		lazy[x*2]+=lazy[x]; lazy[x*2+1]+=lazy[x];
    		lazy[x]=0;
    	}
    	
    	void pushup(int x)
    	{
    		minv[x]=min(minv[x*2],minv[x*2+1]); cnt[x]=0;
    		if (minv[x]==minv[x*2]) cnt[x]+=cnt[x*2];
    		if (minv[x]==minv[x*2+1]) cnt[x]+=cnt[x*2+1];
    	}
    	
    	void update1(int x,int l,int r,int ql,int qr,int v)
    	{
    		if (ql<=l && qr>=r) { minv[x]+=v; lazy[x]+=v; return; }
    		pushdown(x);
    		int mid=(l+r)>>1;
    		if (ql<=mid) update1(x*2,l,mid,ql,qr,v);
    		if (qr>mid) update1(x*2+1,mid+1,r,ql,qr,v);
    		pushup(x);
    	}
    	
    	void update2(int x,int l,int r,int k,int v)
    	{
    		if (l==r) { cnt[x]+=v; return; }
    		pushdown(x);
    		int mid=(l+r)>>1;
    		if (k<=mid) update2(x*2,l,mid,k,v);
    		if (k>mid) update2(x*2+1,mid+1,r,k,v);
    		pushup(x);
    	}
    	
    	int query(int x,int l,int r,int ql,int qr)
    	{
    		if (ql<=l && qr>=r) return (minv[x]==1)?cnt[x]:0;
    		pushdown(x);
    		int mid=(l+r)>>1,res=0;
    		if (ql<=mid) res+=query(x*2,l,mid,ql,qr);
    		if (qr>mid) res+=query(x*2+1,mid+1,r,ql,qr);
    		return res;
    	}
    }seg;
    
    int main()
    {
    	scanf("%d%d",&n,&Q);
    	a[0]=lim;
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		seg.update1(1,0,lim,min(a[i],a[i-1]),max(a[i],a[i-1])-1,1);
    		seg.update2(1,0,lim,a[i],1);
    	}
    	seg.update1(1,0,lim,0,a[n]-1,1);
    	while (Q--)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		seg.update1(1,0,lim,min(a[x],a[x-1]),max(a[x],a[x-1])-1,-1);
    		seg.update1(1,0,lim,min(a[x],a[x+1]),max(a[x],a[x+1])-1,-1);
    		seg.update2(1,0,lim,a[x],-1);
    		a[x]=y;
    		seg.update1(1,0,lim,min(a[x],a[x-1]),max(a[x],a[x-1])-1,1);
    		seg.update1(1,0,lim,min(a[x],a[x+1]),max(a[x],a[x+1])-1,1);
    		seg.update2(1,0,lim,a[x],1);
    		printf("%d\n",seg.query(1,0,lim,1,lim-1));
    	}
    	return 0;
    }
    
  • 相关阅读:
    window忘记密码怎么办
    VS2015配置Andriod开发环境
    记一次 thread.blocked.count 线程过多的问题排查
    Spring的事务初见
    对mybatis的Handler 从使用角度介绍
    最简单的RPC框架实现
    记一次mybatis bindingexception 问题排查
    Java线程池—ThreadPool简介
    [springMvc] 源码分析笔记(二)
    [tomcat] tomcat简析(一)
  • 原文地址:https://www.cnblogs.com/stoorz/p/16378647.html
Copyright © 2020-2023  润新知