[ARC181F] Colorful Reversi

WrongAnswer_90發表於2024-08-08

My Blogs

[ARC181F] Colorful Reversi

首先觀察一下,對於 \(a,b,c,a\) 這種情況來說,兩個 \(a\) 之間永遠不可能發生操作。而 \(a,b,c,b,a\) 這種情況,兩個 \(a\) 之間是有關聯的。有一個很天才的想法是建樹,一開始只有一個節點表示 \(a_1\),維護一個指標 \(pos\) 表示當前在樹上的哪個節點,接下來依次加入每個點 \(a_i\)

  1. \(pos\) 所在點的顏色和 \(a_i\) 相同,則 \(pos\) 不動。
  2. 否則若 \(pos\) 所在點有鄰點的顏色是 \(a_i\),則 \(pos\) 走向該鄰點。
  3. 否則新建一個節點,顏色為 \(a_i\)\(pos\) 走向該節點。

這樣就得到了一棵操作樹,它有一些很好的性質:

  1. 假設生成時在上面走過的路徑是 \(B\),則操作 \(l,r\) 等價於把 \(B[l,r]=\{x,y,y\dots y,y,x\}\) 變成 \(B[l,r]=\{x,x\dots x,x\}\)
  2. 對於原序列,如果能夠操作 \([l,r]\)\(l,r\) 一定屬於同一個點,否則 \(l,r\) 屬於不同的點。
  3. 對於路徑 \(\{v_1,v_2,v_3\dots v_k\}\),透過操作將其變成 \(\{v'_1,v'_2,v'_3\dots v'_k\}\) 的最小代價是 \(\sum_{i=1}^k d(v_i,v'_i)\),其中 \(d(x,y)\) 表示兩點樹上的距離。

接下來,對於 \(pos\) 的起始節點 \(x\) 和終止節點 \(y\),最終序列的形態是若干個顏色段,而顏色恰好就是從 \(x\) 走到 \(y\) 所經過的簡單路徑上的顏色。證明很簡單,顯然路徑上兩個不同的顏色段永遠無法合併,而如果存在其他的顏色則其兩邊的顏色一定是相同的,能再操作一次。

所以拉出樹上 \(x,y\) 之間的路徑,對於此路徑外的部分一定會合併到路徑上,可以先 dfs 一遍算出貢獻,這樣可以得到從 \(B\) 得到一個新的序列 \(C\)。接下來要解決問題的就是:現在有一個序列 \(C[1,n]\),滿足 \(\forall i<n,|C_i-C_{i+1}|\leq 1\)。現在要生成一個新的序列 \(C'\),滿足:

  1. \(C'_1=C_1,C'_n=C_n\)
  2. \(\forall i<n,C'_{i+1}-C'_i\in[0,1]\)
  3. \(\forall i<n\wedge C'_{i+1}=C’_i+1,C_i=C'_i,C_{i+1}=C'_{i+1}\)

在此基礎上滿足 \(\sum_{i=1}^n |C_i-C'_i|\) 最小。設 \(f_{i}\) 表示考慮了 \(\leq i\)\(C\)\(C'_i=C_i\) 時代價的最小值,\(pre_i\) 表示 \(C_i\) 上一次出現的位置。轉移比較簡單:

\[f_i=\min \begin{cases} f_{i-1}\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad C_i=C_{i-1}+1\\ f_{pre_i}+\sum_{j=pre_i}^{i}|C_i-C_j|\;\;\;\;\;pre_i\not=-1 \end{cases} \]

因為 \(pre_i\) 是上一次出現的位置,所以對於所有的 \(j\) 要麼 \(C_j\) 全部大於 \(C_i\),要麼全部小於 \(C_i\),可以字首和處理一下 \(\mathcal O(1)\) 轉移。這樣 \(f_n\) 加上之前把樹縮成一條鏈的代價就是答案,複雜度是 \(\mathcal O(n)\)\(\mathcal O(n\log n)\)

int n,ans,cnt,len,s[500010],lst[500010],vis[500010],real[500010];
int b[500010],c[500010],a[500010],f[500010],t[500010],fa[500010];
map<int,int> T[500010];
void dfs(int x,int Fa)
{
	fa[x]=Fa;
	for(auto [a,y]:T[x])if(y!=Fa)dfs(y,x);
}
void dfs1(int x)
{
	++vis[x];
	for(auto [b,y]:T[x])if(y!=fa[x]&&!vis[y])
	dfs1(y),ans+=a[y],a[x]+=a[y];
}
inline void mian()
{
	read(n);int x,now=1,col;
	read(c[1]),a[cnt=1]=1,col=c[1];
	for(int i=2;i<=n;++i)
	{
		read(c[i]),x=c[i];
		if(x!=col)
		{
			if(!T[now][x])T[now][x]=++cnt,T[cnt][col]=now;
			now=T[now][x],col=x;
		}
		++a[now];
	}
	dfs(1,0);
	while(now)b[++len]=now,vis[now]=1,now=fa[now];
	for(int i=1;i<=len;++i)real[b[i]]=len-i+1,dfs1(b[i]);
	vi ve={0};ve.eb(1),now=1;
	for(int i=2,pos=1;i<=n;++i)
	{
		if(c[i]!=c[i-1])
		{
			assert(T[now][c[i]]);
			now=T[now][c[i]];
		}
		if(vis[now]==2)pos=now;
		ve.eb(real[pos]);
	}
	memset(f,127,sizeof(f)),f[1]=0,s[1]=ve[1],lst[ve[1]]=1;
	for(int i=2;i<(int)ve.size();++i)
	{
		s[i]=s[i-1]+ve[i];
		if(ve[i]==ve[i-1]+1)f[i]=f[i-1];
		int tmp=s[i-1]-s[lst[ve[i]]],v;
		if(ve[i-1]>=ve[i])v=tmp-ve[i]*(i-1-lst[ve[i]]);
		else v=ve[i]*(i-1-lst[ve[i]])-tmp;
		Mmin(f[i],f[lst[ve[i]]]+v);
		lst[ve[i]]=i;
	}
	cout<<f[ve.size()-1]+ans;
}