Loading

CF1270H Number of Components

链接:https://www.luogu.com.cn/problem/CF1270H

题目描述:给一个长度为\(n\)的数组\(a\)\(a\)中的元素两两不同。对于每个数对\((i,j)\),若\(a_{i}<a_{j}\),则让\(i\)\(j\)连一条边。支持\(q\)次修改数组某个位置的值,每次修改后输出图中连通块个数。

题解:这个东西一开始想这维护正反两链,但不行。

考虑每一个连通块一定是连续的一段,因为假如\((i,j,k)\)\((i,k)\)有边,\(j\)孤立,那么因为\(a_{i}<a_{k}\),\(a_{j}<a_{i}\),\(a_{j}>a_{k}\),就矛盾了。

那么对于端点\(p\),有\([l,p]\)全大于\([p+1,r]\)(\(l\)\(p\)端点前的端点,\(r\)\(p\)端点后的端点)

则因为数呈下阶梯状,那么假如\([l,p]\)全大于\([p+1,r]\)\([1,p]\)全大于\([p+1,n]\),假如\([1,p]\)全大于\([p+1,n]\)\([l,p]\)全大于\([p+1,r]\)。两个条件等价。

所以我们就是要求出\([1,p]\)全大于\([p+1,n]\)\(p\)的个数,直接求\(p\)对数不好求,那么我们求\(a_{p}\)满足条件的对数,将大于等于\(a_{p}\)的全设为\(1\),小于\(a_{p}\)的全设为\(0\),那么原序列可看为一个\(01\)序列,且有且仅有一个位置不同。

\(a_{0}=inf\),\(a_{n+1}=0\),则对于每一个\(a_{p}\)\(01\)序列一定会有一个\(01\)分界点且为一个\(01\)分界点时才能产生贡献,所以可用线段树动态维护每一个\(a_{p}\)的分界点。

代码将\(10\)分界点也算进去了,其实是可以不加的。

#include<iostream>
#include<cstdio>
using namespace std;
struct node
{
  int l,r,data,minn,lazy_add;
};
node tree[4000004];
int n,m,a[1000003];
int read()
{
  char c=0;
  int sum=0;
  while (c<'0'||c>'9')
    c=getchar();
  while ('0'<=c&&c<='9')
    {
      sum=sum*10+c-'0';
      c=getchar();
    }
  return sum;
}
void build(int k,int l,int r)
{
  tree[k].l=l;
  tree[k].r=r;
  if (l==r)
      return;
  int mid=(l+r)/2;
  build(k*2,l,mid);
  build(k*2+1,mid+1,r);
  return;
}
void spread(int k)
{
  tree[k*2].minn+=tree[k].lazy_add;
  tree[k*2].lazy_add+=tree[k].lazy_add;
  tree[k*2+1].minn+=tree[k].lazy_add;
  tree[k*2+1].lazy_add+=tree[k].lazy_add;
  tree[k].lazy_add=0;
  return;
}
void push_up(int k)
{
  tree[k].data=0;
  if (tree[k*2].minn<=tree[k*2+1].minn)
    {
      tree[k].data+=tree[k*2].data;
      tree[k].minn=tree[k*2].minn;
    }
  if (tree[k*2].minn>=tree[k*2+1].minn)
    {
      tree[k].data+=tree[k*2+1].data;
      tree[k].minn=tree[k*2+1].minn;
    }
  return;
}
void add(int k,int l,int r,int d)
{
  if (tree[k].l==l&&tree[k].r==r)
    {
      tree[k].lazy_add+=d;
      tree[k].minn+=d;
      return;
    }
  spread(k);
  int mid=(tree[k].l+tree[k].r)/2;
  if (l<=mid&&r<=mid)
    {
      add(k*2,l,r,d);
      push_up(k);
      return;
    }
  if (l>=mid+1&&r>=mid+1)
    {
      add(k*2+1,l,r,d);
      push_up(k);
      return;
    }
  add(k*2,l,mid,d);
  add(k*2+1,mid+1,r,d);
  push_up(k);
  return;
}
void change(int k,int x,int d)
{
  if (tree[k].l==tree[k].r)
    {
      tree[k].data+=d;
      return;
    }
  spread(k);
  int mid=(tree[k].l+tree[k].r)/2;
  if (x<=mid)
    change(k*2,x,d);
  else
    change(k*2+1,x,d);
  push_up(k);
  return;
}
int main()
{
  int x,y;
  n=read(),m=read();
  a[0]=1e6+1;
  build(1,0,1e6);
  for (int i=1;i<=n;++i)
    {
      a[i]=read();
      change(1,a[i],1);
    }
  a[n+1]=0;
  for (int i=0;i<=n;++i)
    {
      if (a[i]<a[i+1])
	add(1,a[i],a[i+1]-1,1);
      else
	add(1,a[i+1],a[i]-1,1);
    }
  for (int i=1;i<=m;++i)
    {
      x=read(),y=read();
      if (a[x]<a[x+1])
        add(1,a[x],a[x+1]-1,-1);
      else
        add(1,a[x+1],a[x]-1,-1);
      if (a[x]<a[x-1])
        add(1,a[x],a[x-1]-1,-1);
      else
        add(1,a[x-1],a[x]-1,-1);
      change(1,a[x],-1);
      change(1,y,1);
      a[x]=y;
      if (a[x]<a[x+1])
        add(1,a[x],a[x+1]-1,1);
      else
        add(1,a[x+1],a[x]-1,1);
      if (a[x]<a[x-1])
        add(1,a[x],a[x-1]-1,1);
      else
        add(1,a[x-1],a[x]-1,1);
      printf("%d\n",tree[1].data);
   }
  return 0;
}
posted @ 2022-12-14 21:55  zhouhuanyi  阅读(48)  评论(0)    收藏  举报