ABC 359

SFlyer發表於2024-06-24

submissions

A,B

直接模擬即可。

C

縱向的距離很好算。有兩種情況:

  • 橫向距離更小。這個直接輸出縱向距離。

  • 更大。減去縱向的步數。

橫向距離怎麼算?我們考慮把 \(s,e\) 都移動到方塊靠左,然後就是橫座標之和。

D

簡單的 dp。設 \(dp_{i,msk}\) 為到了第 \(i\) 為,目前前面的狀態是 \(msk\)A,B 分別是 \(0,1\)。轉移可以預處理也可以直接判斷填 A,B 可不可以。

E

直接單調棧維護目前目前的最大值們。如果有更優(即更靠後切更大)就彈出。

F

考慮到知道每一個都至少有 \(1\),樹就是可以構造出來的,一共要分配 \(2n-2\)。我們先給每一個 \(1\),然後優先佇列貪心分配就可以了。

G

我一個很顯然的資料分治(出現次數 \(<\sqrt{n}\) 的暴力,否則樹上 dp),但是還要 \(\mathcal{O}(1)\) 的 lca,然後我不會。

其實這個題可以直接啟發式合併。每一個節點維護每一個顏色的出現個數。可以把 \(+\)\(-\) 分開來,這樣好算。程式碼也很簡單。

這道題按道理來說虛樹,點分治都可以做,但是這個應該是我見到的最簡潔的寫法。

Code
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 2e5+5;

int n,dep[N],a[N],fr[N];
vector<int> g[N];
map<int,int> mp[N];
ll ans;

void dfs(int u,int fa){
	for (auto v : g[u]){
		if (v^fa){
			dep[v]=dep[u]+1;
			dfs(v,u);
		}
	}
	mp[u][a[u]]=1;
	for (auto v : g[u]){
		if (v^fa){
			if (mp[v].size()>mp[u].size()){
				swap(mp[v],mp[u]);
			}
			for (auto x : mp[v]){
				ans-=1ll*x.second*mp[u][x.first]*dep[u]*2ll;
				mp[u][x.first]+=x.second;
			}
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	for (int i=1; i<n; i++){
		ll u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for (int i=1; i<=n; i++){
		cin>>a[i];
		fr[a[i]]++;
	}
	dfs(1,0);
	for (int i=1; i<=n; i++){
		ans+=1ll*(fr[a[i]]-1)*dep[i];
	}
	cout<<ans<<"\n";
	return 0;
}