痛苦在虚无中回荡 神最终恩赐了绝望 是爱恨交织的冲撞 你永无力再违抗
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 second
using 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_back
using 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_back
using 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;
}

浙公网安备 33010602011771号