• CF1270H Number of Components


    一、题目

    点此看题

    二、解法

    真的好题啊,我这个垃圾感受到了思维的锤炼。

    一开始我想的是做单调栈,我们维护一个递减的单调栈,每次插入一个数就把权值小于它的元素合并到一起,定义合并元素的权值为原来所有元素的权值最小值,连通块个数就是最后栈中元素个数。

    显然单调栈是动态维护不了的,但是我们可以从中看出一个奇妙的性质:连通块对应原序列的一个区间。

    所以可以做一个问题转化,我们求出有多少个分界点 (p) 满足 (forall xin[1,p],yin[p+1,n],a_x>a_y)

    这个问题可以再转成对于原序列中的权值 (v),大于 (v) 的值设置为 (0),小于等于 (v) 的值设置成 (1),求生成 (01) 序列形如这样(111...11000...00) 的权值个数。

    对于每个 (v) 我们维护生成序列 (10) 相邻数对的个数,如果数对个数为 (1) 就是合法的答案。可以用线段树维护,对于原序列上的两个位置 (i,i+1)(vin[min(a_i,a_{i+1}),max(a_i,a_{i+1}))) 的数对个数会增加 (1),区间修改即可。

    为了方便我们设 (a_0=inf,a_{n+1}=-inf),因为 (10) 数对个数至少为 (1),所以我们维护最小值和最小值的数量即可,时间复杂度 (O(nlog n))

    三、总结

    对于排除错误的思路:单调栈是不可能动态维护的(除非特殊情况转成笛卡尔树),想清楚我们要的是什么(本题只需要求元素个数),那么我们把问题转化只关心我们想要的。

    序列图论题的结论思考方向:图论某个量和原序列区间的联系。

    序列大小关系的处理可以转 (01) 序列(比如著名的 (01) 原则),大于某个权值设为 (1),否则设为 (0),然后研究 (01) 序列的性质。

    维护某一个特定值的数量,思考他是否一定是最值,是的话转成维护最值和最值的数量。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int inf = 0x3f3f3f3f;
    const int M = 1000005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,q,a[M],ad[4*M];
    struct node
    {
    	int v,c;
    	node(int V=inf,int C=0) : v(V) , c(C) {}
    	node operator + (const node &b) const
    	{
    		node r;
    		r.v=min(v,b.v);
    		if(r.v==v) r.c+=c;
    		if(r.v==b.v) r.c+=b.c;
    		return r;
    	}
    }tr[4*M];
    void add(int i,int x)
    {
    	ad[i]+=x;tr[i].v+=x;
    }
    void down(int i)
    {
    	if(!ad[i]) return ;
    	add(i<<1,ad[i]);
    	add(i<<1|1,ad[i]);
    	ad[i]=0;
    }
    void up(int i)
    {
    	tr[i]=tr[i<<1]+tr[i<<1|1];
    }
    void ins(int i,int l,int r,int id,int f)
    {
    	if(l==r)
    	{
    		if(f==0) tr[i]=node(inf,0);
    		else tr[i]=node(ad[i],1);
    		return ; 
    	}
    	int mid=(l+r)>>1;down(i);
    	if(mid>=id) ins(i<<1,l,mid,id,f);
    	else ins(i<<1|1,mid+1,r,id,f);
    	up(i);
    }
    void upd(int i,int l,int r,int L,int R,int x)
    {
    	if(L>r || l>R) return ;
    	if(L<=l && r<=R)
    	{
    		add(i,x);
    		return ;
    	}
    	int mid=(l+r)>>1;down(i);
    	upd(i<<1,l,mid,L,R,x);
    	upd(i<<1|1,mid+1,r,L,R,x);
    	up(i);
    }
    void work(int i,int f)
    {
    	int l=min(a[i],a[i+1]),r=max(a[i],a[i+1]);
    	upd(1,0,m,l,r-1,f);
    }
    int main()
    {
    	n=read();m=1e6;q=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read(),ins(1,0,m,a[i],1);
    	a[0]=m+1;a[n+1]=0;
    	for(int i=0;i<=n;i++) work(i,1);
    	while(q--)
    	{
    		int x=read();
    		//delete
    		work(x-1,-1);work(x,-1);ins(1,0,m,a[x],0);
    		//add
    		a[x]=read();
    		work(x-1,1);work(x,1);ins(1,0,m,a[x],1);
    		if(tr[1].v==1) printf("%d
    ",tr[1].c);
    		else puts("0");
    	}
    }
    
  • 相关阅读:
    02-模板字符串
    01-学习vue前的准备工作
    21-z-index
    20-定位
    19-background
    18-超链接导航栏案例
    17-文本属性和字体属性
    16-margin的用法
    jBPM
    Table of Contents
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15244536.html
Copyright © 2020-2023  润新知