test38
老师,给我们做这种模拟赛良心不会痛吗?
重建道路
首先你选边肯定不能破坏原有的连通性不然次数就不是最小的了,然后考虑初始连通块的最小点分别是 \(p_1,\dots,p_m\),你会选择依次将 \(p_1\) 与 \(p_2,\dots,p_m\) 连接。现在只用考虑删掉的边集最好是什么, 你想顺着字典序依次枚举每一条边,如果删除不影响连通性就用来删除,但这个过程不好维护,不过显然你可以贪心,倒着字典序做能加就加肯定不劣,剩下的边就是可以选的。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define fir first
#define sec secondusing namespace std;const int N=200005;int T, n, m, dsu[N];
struct node {int x, y;bool operator<(const node &rhs) const { return x==rhs.x?y<rhs.y:x<rhs.x; }
} p[N];int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }void mian() {cin >> n >> m;up(i,1,m) {cin >> p[i].x >> p[i].y;if(p[i].y<p[i].x) swap(p[i].x,p[i].y);}sort(p+1,p+1+m);up(i,1,n) dsu[i]=i;dn(i,m,1) {int x=get(p[i].x), y=get(p[i].y);if(x==y) continue;dsu[y]=x, p[i].x=p[i].y=0;}int j=0, u=1;vector<pair<pii,pii> > ans;up(i,1,n) if(dsu[i]==i) {if(j) {while(u<=m&&!p[u].x) ++u;if(u>m) {cout << -1 << '\n';return;}ans.pb(mp(mp(p[u].x,p[u].y),mp(j,i)));++u;}else j=i;}cout << ans.size() << '\n';for(pair<pii,pii> u:ans) cout << u.fir.fir << ' ' << u.fir.sec << ' ' << u.sec.fir << ' ' << u.sec.sec << '\n';
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cin >> T;while(T--) mian();return 0;
}
随机爬树
你这个保证 \(sum_u\) 不是 \(P\) 的倍数。
对每一个 \(a_i\) 考虑贡献,是 \(a_i\times \prod_{u\to i} \frac{w_i}{sum_{fa_u}}\),累乘再累加,\(w_u\) 对 \(u\) 的子树贡献,\(sum_u\) 对子树中除了根贡献,这些在 dfn 上面是点/区间,线段树维护即可。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_back
#define ls(p) (p<<1)
#define rs(p) (p<<1|1) using namespace std;const int N=100005, P=998244353;inline int ksm(int a,int b=P-2) {int ret=1;for( ; b; b>>=1) {if(b&1) ret=ret*a%P;a=a*a%P;}return ret;
}int n, T, stp, fa[N], L[N], R[N], w[N], sum[N], a[N];
int tr[N<<2], mul[N<<2];
vector<int> to[N];void dfs(int x) {L[x]=++stp;for(int y:to[x]) (sum[x]+=w[y])%=P, dfs(y);R[x]=stp;
}inline void tup(int p) {tr[p]=(tr[ls(p)]+tr[rs(p)])%P;
}inline void tdn(int p) {(mul[ls(p)]*=mul[p])%=P, (tr[ls(p)]*=mul[p])%=P;(mul[rs(p)]*=mul[p])%=P, (tr[rs(p)]*=mul[p])%=P;mul[p]=1;
}void build(int p=1,int s=1,int e=n) {mul[p]=1;if(s==e) { tr[p]=1; return; }int mid=(s+e)>>1;build(ls(p),s,mid), build(rs(p),mid+1,e);tup(p);
}void modify(int l,int r,int v,int p=1,int s=1,int e=n) {if(l<=s&&e<=r) {(mul[p]*=v)%=P, (tr[p]*=v)%=P;return;}tdn(p);int mid=(s+e)>>1;if(l<=mid) modify(l,r,v,ls(p),s,mid);if(r>mid) modify(l,r,v,rs(p),mid+1,e);tup(p);
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cin >> n;up(i,2,n) {cin >> fa[i];to[fa[i]].pb(i);}up(i,1,n) cin >> w[i];up(i,1,n) cin >> a[i];dfs(1), build();up(i,1,n) {modify(L[i],L[i],a[i]);if(i>1) modify(L[i],R[i],w[i]);if(sum[i]) {modify(L[i],R[i],ksm(sum[i]));modify(L[i],L[i],sum[i]); }}cout << (tr[1]%P+P)%P << '\n';cin >> T;while(T--) {int u, W, A;cin >> u >> W >> A;modify(L[u],L[u],ksm(a[u])*A%P), a[u]=A;if(u>1) {int nxt=((sum[fa[u]]+(W-w[u]))%P+P)%P;modify(L[fa[u]],R[fa[u]],sum[fa[u]]*ksm(nxt)%P);modify(L[fa[u]],L[fa[u]],ksm(sum[fa[u]])*nxt%P);sum[fa[u]]=nxt;modify(L[u],R[u],ksm(w[u])*W%P), w[u]=W;}cout << (tr[1]%P+P)%P << '\n';}return 0;
}
交集
你维护出某个点在左边/右边的连通块,两个并查集祖先都相等的可以相互访问。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_backusing namespace std;const int N=300005;int n, ml, mr, ans[N], cnt[N];
vector<int> sav[N];
struct DSU {int dsu[N];void clear() {up(i,1,n) dsu[i]=i;}int get(int x) {if(x==dsu[x]) return x;return dsu[x]=get(dsu[x]);}void merge(int x,int y) {x=get(x), y=get(y);if(x!=y) dsu[x]=y;}
} L, R;signed main() {ios::sync_with_stdio(0);cin.tie(0);cin >> n >> ml >> mr;L.clear(), R.clear();while(ml--) {int u, v;cin >> u >> v;L.merge(u,v);}while(mr--) {int u, v;cin >> u >> v;R.merge(u,v);}up(i,1,n) sav[L.get(i)].pb(i);up(i,1,n) if(sav[i].size()) {int res=0;for(int u:sav[i]) ++cnt[R.get(u)];for(int u:sav[i]) ans[u]=cnt[R.get(u)];for(int u:sav[i]) --cnt[R.get(u)];}up(i,1,n) cout << ans[i] << ' ';return 0;
}
地铁换乘
\(c\) 相等的且连在一起的边有特殊贡献,具体而言买一次票两两通达,边又没多少你直接你新建一个出入代价都为 \(\frac{p_c}{2}\) 的点连上去就行了。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define pb push_backusing namespace std;const int N=300005, M=1000005, inf=1e18;int n, m, C, p[M], dsu[N], tot, dis[N], tag[N];
struct node { int x, y, w; } e[N];
vector<pii> to[N];
vector<int> gp[N];int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }signed main() {ios::sync_with_stdio(0);cin.tie(0);cin >> n >> m >> C, tot=n;up(i,1,C) cin >> p[i];up(i,1,m) cin >> e[i].x >> e[i].y >> e[i].w;sort(e+1,e+1+m,[](node i,node j){return i.w<j.w;});for(int l=1, r=1; l<=m; l=r+1, r=l) {while(r<m&&e[r+1].w==e[l].w) ++r;set<int> sav;up(i,l,r) {int x=e[i].x, y=e[i].y;dsu[x]=x, sav.insert(x);dsu[y]=y, sav.insert(y);}up(i,l,r) {int x=get(e[i].x), y=get(e[i].y);if(x!=y) dsu[x]=y;}for(int u:sav) gp[get(u)].pb(u);for(int u:sav) if(gp[u].size()) {++tot;for(int i:gp[u]) {to[i].pb(mp(tot,p[e[l].w]));to[tot].pb(mp(i,p[e[l].w]));}gp[u].clear();}}up(i,2,tot) dis[i]=inf;priority_queue<pii,vector<pii>,greater<pii> > q;q.push(mp(0,1));while(q.size()) {int x=q.top().second;q.pop();if(tag[x]) continue;tag[x]=1;for(pii u:to[x]) {int y=u.first, w=u.second;if(dis[x]+w<dis[y]) {dis[y]=dis[x]+w;q.push(mp(dis[y],y));}}}up(i,1,n) cout << (dis[i]==inf?-1:dis[i]/2) << ' ';return 0;
}