Description
Input
Output
對於每個詢問操作,輸出滿足條件的子區間數
Sample Input
3
1 2 3
3
0
1 1 2
0
Sample Output
6
4
Solution
對於每個位置,考慮以這個位置開頭的子區間,最右能延伸到哪裏,設爲suf[i]。
爲了維護這個,設一個next[i]表示第i個位置之後的第一個同顏色的位置再哪裏
那麼suf[i]就是next[i]的後綴min
轉換成模型:對於給定序列,這個序列構成的單調棧的權值和(也可以是最大值,最小值等)
聽說這個模型是wc2013的樓房重建?
設find(i,j,p)表示在[i,j]區間後面加入一個p之後的單調棧的權值和
在這題中,單調棧是單調上升的
那麼對於區間的右半邊,求出最小值rmin
如果
否則,右邊被完全彈出,答案是find(i,mid,p)+p*(j-m)
如果i=j,直接考慮這個點是否會被p彈掉,輸出p或本身值
但是這樣會超時
設fm[i,j]表示將[i,j]區間的右邊最小值加入左邊的權值和,即find(i,mid,rmin)
這樣在find的時候直接調用這個,是O(1)的,查詢次數是log的
在每次修改時維護這個fm,也是會修改log次
最終複雜度是
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 501000
#define INF 1010580540
#define ll long long
using namespace std;
int a[N],n,b[N],nxt[N];
ll t[N],g[N],fm[N];
set<int> c[N];
typedef set<int> :: iterator it;
ll find(ll v,ll x,ll y,ll p)
{
if(x==y) return g[v]<=p?t[v]:p;
ll m=(x+y)/2,rm=g[v*2+1];
if(p<=rm) return find(v*2,x,m,p)+p*(y-m);
return fm[v]+find(v*2+1,m+1,y,p);
}
void ins(int v,int i,int j,int x,int y)
{
if(i==j)
{
t[v]=g[v]=y;
return;
}
int m=(i+j)/2;
if(x<=m) ins(v*2,i,m,x,y);
else ins(v*2+1,m+1,j,x,y);
t[v]=t[v*2]+t[v*2+1];
g[v]=min(g[v*2],g[v*2+1]);
fm[v]=find(v*2,i,m,g[v*2+1]);
}
int main()
{
memset(g,60,sizeof(g));
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]),c[a[i]].insert(i),c[i].insert(n+1),c[i].insert(0),b[i]=n+1;
fd(i,n,1)
ins(1,1,n,i,nxt[i]=b[a[i]]),b[a[i]]=i;
int ac;scanf("%d",&ac);
while(ac--)
{
int z;scanf("%d",&z);
if(z==0)
{
ll ans=find(1,1,n,n+1);
printf("%lld\n",ans-(ll)((ll)n*(ll)(n+1)/2));
}
else
{
int x,y;scanf("%d%d",&x,&y);
int q=a[x];
it pos=c[q].find(x);
int z=*(--pos);
nxt[z]=nxt[x];c[q].erase(x);
if(z>0) ins(1,1,n,z,nxt[z]);
a[x]=y;q=y;c[q].insert(x);
pos=c[q].find(x);
z=*(--pos);nxt[z]=x;
if(z>0) ins(1,1,n,z,nxt[z]);
pos++;
z=*(++pos);nxt[x]=z;
ins(1,1,n,x,z);
}
}
}