题目大意
ij之间有边当且仅当i<j且a[i]<a[j],动态修改a,求连通块个数
题解
线段树吼题
转换一:把求连通块个数变成求分界线个数
设a[0]=inf,a[n+1]=-inf
分界线的定义:min(a[1...i])>max(a[i+1...n])
转换二:设h[i][j]=[a[j]>a[i]],则点i的a[i]能把图分成两块当且仅当h[i]中前面一段是1,后面一段是0
手玩一下即可,刚好可以按十字分成左上和右下
于是变成维护有多少个i满足h[i]只有两段,等于维护h[a[i]]中 10子串 个数
直接维护所有h[i],同时维护i是否存在,修改只与a[x-1]、a[x]、a[x+1]和y有关(影响是一段区间)
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define ll long long
#define N 1000000
//#define file
using namespace std;
int tr[4000001][2],Tr[4000001],a[500002],n,Q,i,j,k,l,x,y;
void down(int t,int len)
{
if (Tr[t])
{
if (len>1) Tr[t*2]+=Tr[t],Tr[t*2+1]+=Tr[t];
tr[t][0]+=Tr[t];
Tr[t]=0;
}
}
void up(int t)
{
if (tr[t*2][0]<tr[t*2+1][0])
tr[t][0]=tr[t*2][0],tr[t][1]=tr[t*2][1];
else
if (tr[t*2][0]>tr[t*2+1][0])
tr[t][0]=tr[t*2+1][0],tr[t][1]=tr[t*2+1][1];
else
tr[t][0]=tr[t*2][0],tr[t][1]=tr[t*2][1]+tr[t*2+1][1];
}
void change(int t,int l,int r,int x,int y,int s)
{
x=max(x,1);y=min(y,N);
if (x>y) return;
int mid=(l+r)/2;
down(t,r-l+1);
if (x<=l && r<=y) {Tr[t]+=s;down(t,r-l+1);return;}
down(t*2,mid-l+1);down(t*2+1,r-mid);
if (x<=mid) change(t*2,l,mid,x,y,s);
if (mid<y) change(t*2+1,mid+1,r,x,y,s);
up(t);
}
void Change(int t,int l,int r,int x,int s)
{
int mid=(l+r)/2;
down(t,r-l+1);
if (l==r) {tr[t][1]+=s;return;}
down(t*2,mid-l+1);down(t*2+1,r-mid);
if (x<=mid) Change(t*2,l,mid,x,s);
else Change(t*2+1,mid+1,r,x,s);
up(t);
}
int main()
{
#ifdef file
freopen("CF1270H.in","r",stdin);
#endif
scanf("%d%d",&n,&Q);
a[0]=1919810;a[n+1]=-1919810;
fo(i,1,n) scanf("%d",&a[i]),Change(1,1,N,a[i],1);
fo(i,0,n) if (a[i]>a[i+1]) change(1,1,N,a[i+1],a[i]-1,1);
for (;Q;--Q)
{
scanf("%d%d",&x,&y);
Change(1,1,N,a[x],-1);
if (a[x-1]>a[x]) change(1,1,N,a[x],a[x-1]-1,-1);
if (a[x]>a[x+1]) change(1,1,N,a[x+1],a[x]-1,-1);
a[x]=y;
Change(1,1,N,a[x],1);
if (a[x-1]>a[x]) change(1,1,N,a[x],a[x-1]-1,1);
if (a[x]>a[x+1]) change(1,1,N,a[x+1],a[x]-1,1);
printf("%d
",tr[1][1]);
}
fclose(stdin);
fclose(stdout);
return 0;
}