SMU 2024 ptlks的周报Week 9 (7.15-7.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;
}