SMU 2024 ptlks的週報Week 9 (7.15-7.21)

ptlks發表於2024-07-21

這周學了啟發式合併,prim演算法,對圖有了進一步的理解。

題意:給一棵根為 1 的有根樹,點 i 具有一個權值 \(A_i\)定義一個點對的值 f(u,v)=max(\(A_u\),\(A_v\))×∣\(A_u\)-\(A_v\)∣ 。你需要對於每個節點 i ,計算 \(ans_i=\displaystyle\sum_{u,v∈subtree(i)}f(u,v)\) ,其中 subtree(i) 表示 i 的子樹。請你輸出 ⊕($ans_i\ mod\ 2^{64}) $ ,其中 ⊕ 表示 XOR。

思路:樹上啟發式合併+線段樹

程式碼

#include <bits/stdc++.h>
#define int unsigned long long
#define ll unsigned long long
#define mod 1000000007
#define PII pair<int,int>
#define PIII pair<PII,int>
#define double long double
#define endl '\n'
#pragma GCC optimize(3,"Ofast","inline")

using namespace std;

const int N = 1e6 + 5, SZ = N << 2;

ll a[N];
int c[N], vis[N];
vector<int>g[N];
int dp[N][2];
int s1, s2;
int mn =0, mx, sum, ss2;
int n, k, m;
set<PII>qqq;
int qpow(int x, int y) {
	int ans = 1;
	while (y) {
		if (y & 1)ans = (ans * x) % mod;
		x = (x * x) % mod;
		y >>= 1;
	}
	return ans;
}

int pos[N];
struct data {
	int tad,cnt;
	ll sum,sum2;
	ll max, min;
} t[N << 2];

void push_up(int u) {
	t[u].cnt = (t[u << 1].cnt + t[u << 1 | 1].cnt);
	t[u].sum = (t[u << 1].sum + t[u << 1 | 1].sum);
	t[u].sum2 = (t[u << 1].sum2 + t[u << 1 | 1].sum2);
	t[u].max = max(t[u << 1].max, t[u << 1 | 1].max);
	t[u].min = min(t[u << 1].min, t[u << 1 | 1].min);
}



void build(int u = 1, int l = 1, int r = N) {
	if (l == r) {
		pos[l] = u;
		t[u].cnt=c[l];
		t[u].sum=l*c[l];
		t[u].sum2=l*l*c[l];
		t[u].max = l;
		t[u].min = l;
		return;
	}
	int mid = (l + r) >> 1;
	build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
	push_up(u);
}

void add(int p) {
	int u = pos[p];
	t[u].cnt++;
	t[u].max = p;
	t[u].min = p;
	t[u].sum+=p;
	t[u].sum2+=p*p;
	while (u >>= 1)push_up(u);
}

void del(int p) {
	int u = pos[p];
	t[u].cnt--;
	t[u].sum-=p;
	t[u].sum2-=p*p;
	if (!t[u].cnt) {
		t[u].max = 0;
		t[u].min=1e9;
	}
	while (u >>= 1)push_up(u);
}

ll qmax(int L, int R, int u = 1, int l = 1, int r = N) {
	if (R < l || r < L) return 0;
	if (L <= l && r <= R) return t[u].max;
	int mid = (l + r) >> 1;
	return max(qmax(L, R, u << 1, l, mid) , qmax(L, R, u << 1 | 1, mid + 1, r));
}

ll qsum(int L, int R, int u = 1, int l = 1, int r = N) {
	if (R < l || r < L) return 0;
	if (L <= l && r <= R) return t[u].sum;
	int mid = (l + r) >> 1;
	return (qsum(L, R, u << 1, l, mid) + qsum(L, R, u << 1 | 1, mid + 1, r));
}

ll qsum2(int L, int R, int u = 1, int l = 1, int r = N) {
	if (R < l || r < L) return 0;
	if (L <= l && r <= R) return t[u].sum2;
	int mid = (l + r) >> 1;
	return (qsum2(L, R, u << 1, l, mid) + qsum2(L, R, u << 1 | 1, mid + 1, r));
}

ll qcnt(int L, int R, int u = 1, int l = 1, int r = N) {
	if (R < l || r < L) return 0;
	if (L <= l && r <= R) return t[u].cnt;
	int mid = (l + r) >> 1;
	return (qcnt(L, R, u << 1, l, mid) + qcnt(L, R, u << 1 | 1, mid + 1, r));
}

ll qmin(int L, int R, int u = 1, int l = 1, int r = N) {
	if (R < l || r < L) return 1e9;
	if (L <= l && r <= R) return t[u].min;
	int mid = (l + r) >> 1;
	return min(qmin(L, R, u << 1, l, mid) , qmin(L, R, u << 1 | 1, mid + 1, r));
}

int sz[N], big[N], L[N], R[N], Node[N], totdfn;
int cnt[N], totColor;
ll ans[N];

void dfs0(int u, int p) {
	L[u] = ++totdfn;
	Node[totdfn] = u;
	sz[u] = 1;
	for (int v : g[u])
		if (v != p) {
			dfs0(v, u);
			sz[u] += sz[v];
			if (!big[u] || sz[big[u]] < sz[v]) big[u] = v;
		}
	R[u] = totdfn;
}

void dfs1(int u, int p, bool keep) {
	for (int v : g[u])
		if (v != p && v != big[u]) {
			dfs1(v, u, false);
		}
	if (big[u]) {
		dfs1(big[u], u, true);
	}
	for (int v : g[u])
		if (v != p && v != big[u]) {
			for (int i = L[v]; i <= R[v]; i++) {
				ans[u]+=2*qcnt(1,a[Node[i]])*a[Node[i]]*a[Node[i]];
//				cout<<qcnt(1,a[Node[i]])*a[Node[i]]*a[Node[i]]<<endl;
				ans[u]+=2*qsum2(a[Node[i]]+1,N);
//				cout<<qsum2(a[Node[i]]+1,N)<<endl;
				ans[u]-=2*qsum(1,N)*a[Node[i]];
//				cout<<qsum(1,N)*a[Node[i]]<<endl;
				add(a[Node[i]]);
//				cout<<a[Node[i]]<<endl;
//				for (int l = 1; l <= 10; l++) {
//					cout<<qsum(l,l)<<" \n"[l==10];
//				}
			}
		}
	
	ans[u]+=2*qcnt(1,a[u])*a[u]*a[u];
//	cout<<qcnt(1,a[u])*a[u]*a[u]<<endl;
	ans[u]+=2*qsum2(a[u]+1,N);
//	cout<<qsum2(a[u]+1,N)<<endl;
	ans[u]-=2*qsum(1,N)*a[u];
//	cout<<qsum(1,N)*a[u]<<endl;
	add(a[u]);
//	cout<<"ans"<<u<<' '<<ans[u]<<endl;
//	cout<<a[u]<<endl;
//	for (int i = 1; i <= 10; i++) {
//		cout<<qsum(i,i)<<" \n"[i==10];
//	}
	if (keep == false) {
		for (int i = L[u]; i <= R[u]; i++) {
			del(a[Node[i]]);
		}
	}else{
		ans[p]+=ans[u];
	}
}


void solve() {
	int s;
	cin >> n ;
	
	for (int i = 1; i < n; i++) {
		int u, v, x;
		cin >> u >> v;
		g[u].emplace_back(v);
		g[v].emplace_back(u);
	}
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
//	for (int i = 1; i <= 10; i++) {
//		cout<<qcnt(i,i)<<" \n"[i==10];
//	}
	build();
	dfs0(1, 0);
	dfs1(1, 0, false);
	ll Ans=0;
	for (int i = 1; i <= n; i++) {
		Ans^=ans[i];
//		cout<<ans[i]<<" \n"[i==n];
	}
	cout<<Ans<<endl;

}

int32_t main() {
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int T = 1;
	//cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}

MST公司

題意:你面對的是一個加權無向圖,它包含n頂點 和m沒有任何自環的邊緣。Sajin 呈現q查詢。對於每個頂點集S是給出的。您的目標是確定S並找到其最小生成樹的權重。最小生成樹 (MST) 是連線的邊加權圖的邊的子集,它以儘可能小的總邊權重將所有頂點連線在一起,無需任何迴圈。在圖論的數學領域中,圖的誘導子圖是另一個圖,由圖的頂點子集和原始圖中的所有邊組成,連線該子集中的頂點對。如果誘導子圖S已斷開連線,輸出 -1。

思路:根號分治+prim演算法

程式碼

#include <bits/stdc++.h>
#define ll long long
//#define int long long
#define mod 1000000007
#define PII pair<int,ll>
#define PIII pair<PII,int>
#define double long double
#define endl '\n'
#pragma GCC optimize(3,"Ofast","inline")

using namespace std;

const int N = 1e5 + 5, SZ = N << 2;

int k;
int n, m, h[N], cnte;
int a[N], b[N];
vector<PII>g[N], G[N];
struct S {
	int u;
	ll d;
};

bool operator<(const S &x, const S &y) {
	return x.d > y.d;
}

priority_queue<S> q;
ll dis[N];
bool vis[N];

ll res = 0, cnt = 0;

void Prim(int x) {
	res = 0, cnt = 0;
	for (int i = 0; i < k; i++) {
		dis[b[i]] = 1e18;
		vis[b[i]] = 0;
	}
	dis[x] = 0;
	while (q.size())q.pop();
	q.push({x, 0});
	while (!q.empty()) {
		if (cnt >= n) break;
		int u = q.top().u, d = q.top().d;
//		cout << u << ' ' << d << endl;
		q.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		++cnt;
		res += d;
		for (auto[v,w] : g[u]) {
//			cout << v << ' ' << w << 'x' << endl;
			if (w < dis[v] && a[v]) {
				dis[v] = w, q.push({v, w});
			}
		}
	}
}

void Prim1(int x) {
	res = 0, cnt = 0;
	for (int i = 0; i < k; i++) {
		dis[b[i]] = 1e18;
//		cout<<' '<<b[i]<<endl;
		vis[b[i]] = 0;
	}
	dis[x] = 0;
	while (q.size())q.pop();
	q.push({x, 0});
	while (!q.empty()) {
		if (cnt >= n) break;
		int u = q.top().u, d = q.top().d;
		
		q.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		++cnt;
		res += d;
//		cout << u << ' ' << d <<' '//<<res<< endl;
		for (auto[v, w] : G[u]) {
//			cout << v << ' ' << w << 'y' << endl;
//			cout<< dis[v] <<' '<<a[v]<<endl;
			
			if (w < dis[v] && a[v]) {
//				cout<<'t'<<endl;
				dis[v] = w, q.push({v, w});
			}
		}
	}
}

void solve() {
	int q;
	cin >> n >> m >> q;
	for (int i = 1; i <= m; i++) {
		int u, v, x;
		cin >> u >> v >> x;
		g[u].push_back({v,x});
		g[v].push_back({u,x});
	}
	for(int i=1;i<=n;i++){
		sort(g[i].begin(),g[i].end());
	}
	int nn = 3e2;
	
	for (int i = 0; i < q; i++) {
		cin >> k;
		for (int j = 0; j < k; j++) {
			int x;
			cin >> x;
			a[x] = 1;
			b[j] = x;
		}
		if (k < nn) {
			for (int j = 0; j < k; j++) {
				for (int l = 0; l < k; l++) {
					if(j==l)continue;
					PII op={b[l],0};
					int pp=lower_bound(g[b[j]].begin(),g[b[j]].end(),op)-g[b[j]].begin();;
					if(pp<g[b[j]].size()&&a[g[b[j]][pp].first]){
						
						//cout<<g[b[j]][pp].first<<' '<<g[b[j]][pp].second<<endl;
						G[b[j]].push_back(g[b[j]][pp]);
					}
					
				}
			}
		}
		if (k < nn) {
			Prim1(b[0]);
			for (int i = 0; i < k; i++) {
				G[b[i]].clear();
			}
		} else {
			Prim(b[0]);
		}
		for (int j = 0; j < k; j++) {
			a[b[j]]=0;
		}
		if (cnt == k)cout << res << endl;
		else cout << -1 << endl;
//		cout << endl;
	}
	
}

int32_t main() {
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int T = 1;
	//cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}

相關文章