题解 给国与地震

传送门

先来口胡一个场上没写完的做法:
考虑根号分治,以 \(\sqrt m\) 为界
一个暴力是合并时枚举两端点连通块的所有出边
但是度数很大的端点肯定不能直接枚举
那就根号分治,对度数 \(>\sqrt m\) 的连通块开 \(n\) 个优先队列
\(i\) 个按 \(s\) 升序存与点 \(i\) 的连边
然后对每个大点再开一个 set 存这个连通块每个优先队列中 \(s_i-siz_v\) 最小的边
合并两个优先队列是 \(O(\min\{siz\}\log n)\)
那么合并两个(大)连通块是 \(O(n\sqrt m+\min\{\deg\}\log n)\)
合并一大一小两个连通块的时候暴力并进来
因为是在合并连通块,所以对度数套类似启发式合并的复杂度证明可得均摊 \(m\log^2 n\)
合并两个小连通块也是暴力合并边(可能导致其变为一个大连通块)
这些合并导致的 \(siz\) 变化也是可以维护的
大连通块 \(siz\) 的变化直接打标记
小连通块 \(siz\) 变化在所有与之相邻的大连通块对应的优先队列上打标记
虽然可能导致 set 的插入,但仍可以依靠每条边最多产生一次影响均摊下去
所以总复杂度是 \(O(n\sqrt m+m\log^2n)\)
空间会很卡,\(\tt priority\_queue\) 可能需要在第一次有值的时候再申请
然后听说有人写 \(O(n\sqrt m\log n)\) 跑过去了,最大的点 200 ms
感觉亏死了 /kk

然后正解:
还是从上面那个暴力入手
考虑这样一个性质:若一条边还需要 \(s_i\),则若两端点的变化量都 \(<\frac{s_i}{2}\),则此边一定不合法,不必 check
那么用 set 启发式合并维护边集,按还需要\(s_i\) 排序
若一次合并导致的变化量为 \(k\),只考虑 \(k\geqslant\frac{s_i}{2}\) 的出边,然后更新其临界值
发现每次更新一条边都会使其临界值减半,所以每条边最多更新 \(O(\log n)\)
复杂度 \(O((n+m)\log^2 n)\)
然后加反向边的时候忘记把 it->to 改成 u 调了一个小时

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
ll a[N];
int deg[N];
struct edge{int fir, sec, val;}e[N<<1];

namespace force{
	ll siz[N];
	bool del[N];
	int dsu[N], sta[N], top;
	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
	inline void uni(int s, int t) {if ((s=find(s))!=(t=find(t))) siz[dsu[s]=t]+=siz[s];}
	void solve() {
		for (int i=1; i<=n; ++i) dsu[i]=i, siz[i]=a[i];
		jump: ;
		for (int i=1; i<=m; ++i) if (!del[i]) {
			if (find(e[i].fir)==find(e[i].sec)) del[i]=1;
			else {
				if (e[i].val<=siz[find(e[i].fir)]+siz[find(e[i].sec)]) {
					del[sta[++top]=i]=1;
					uni(e[i].fir, e[i].sec);
					goto jump;
				}
			}
		}
		printf("%d\n", top);
		for (int i=1; i<=top; ++i) printf("%d%c", sta[i], " \n"[i==top]);
	}
}

// namespace task1{
// 	ll siz[N];
// 	int dsu[N], sta[N], top;
// 	int id[N], val[N], sqr, tot;
// 	vector<pair<int, int>> to[N];
// 	priority_queue<int, vector<int>, greater<int>> que;
// 	struct point{
// 		int id;
// 		bool vis[100005];
// 		set<pair<int, int>> s;
// 		struct cmp{inline bool operator () (int a, int b) {return val[a]<val[b];}};
// 		priority_queue<int, vector<int>, cmp> *q[100005];
// 		inline void push(int pos, int id) {
// 			if (!q[pos]) q[pos]=new priority_queue<int, vector<int>, cmp>;
// 			q[pos]->push(id);
// 		}
// 		void build() {
// 			for (int i=1; i<=n; ++i) if (q[i]&&q[i]->size())
// 				s.insert({val[q[i]->top()]-siz[i], i});
// 		}
// 		void check() {
// 			while (s.size() && siz[id]>=s.begin()->fir) {
// 				int v=s.begin()->sec;
// 				s.erase(s.begin());
// 				while (q[v]->size() && siz[id]+siz[v]>=val[q[v]->top()])
// 					que.push(q[v]->top()), q[v]->pop();
// 				if (q[v]->size()) s.insert()
// 			}
// 		}
// 	}p[250];
// 	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
// 	inline void uni(int s, int t) {if ((s=find(s))!=(t=find(t))) siz[dsu[s]=t]+=siz[s];}
// 	void solve() {
// 		cout<<double(sizeof(p))/1000/1000<<endl;
// 		sqr=sqrt(m); // 记得调成 800+
// 		for (int i=1; i<=m; ++i) val[i]=e[i].val;
// 		for (int i=1; i<=m; ++i) to[e[i].fir].pb({e[i].sec, i}), to[e[i].sec].pb({e[i].fir, i});
// 		for (int i=1; i<=n; ++i) if (to[i].size()>sqr) {
// 			id[i]=++tot;
// 			for (auto& it:to[i]) p[tot].push(it.fir, val[it.sec]);
// 			p[tot].build();
// 		}

// 	}
// }

namespace task1{
	ll siz[N];
	int sta[N], top;
	set<pair<int, int>> s;
	priority_queue<int, vector<int>, greater<int>> q;
	void solve() {
		for (int i=1; i<=n; ++i) siz[i]=a[i];
		for (int i=1; i<=m; ++i) {
			int u=e[i].fir, v=e[i].sec;
			if (u!=1) swap(u, v), swap(e[i].fir, e[i].sec);
			s.insert({e[i].val-siz[v], i});
		}
		while (1) {
			while (s.size() && siz[1]>=s.begin()->fir) {
				q.push(s.begin()->sec);
				s.erase(s.begin());
			}
			if (q.size()) sta[++top]=q.top(), siz[1]+=siz[e[q.top()].sec], q.pop();
			else break;
		}
		printf("%d\n", top);
		for (int i=1; i<=top; ++i) printf("%d%c", sta[i], " \n"[i==top]);
	}
}

namespace task2{
	ll siz[N], val[N];
	int dsu[N], sta[N], top;
	vector<pair<int, int>> to[N];
	priority_queue<int, vector<int>, greater<int>> q;
	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
	inline void uni(int s, int t) {if ((s=find(s))!=(t=find(t))) siz[dsu[s]=t]+=siz[s];}
	void solve() {
		for (int i=1; i<=n; ++i) dsu[i]=i, siz[i]=a[i];
		for (int i=1; i<=m; ++i) {
			val[i]=e[i].val;
			to[e[i].fir].pb({e[i].sec, i});
			to[e[i].sec].pb({e[i].fir, i});
		}
		for (int i=1; i<=n; ++i) {
			for (int j=0; j<to[i].size(); ++j) if (siz[i]+siz[to[i][j].fir]>=val[to[i][j].sec]) {
				q.push(to[i][j].sec);
				swap(to[i][j], to[i][to[i].size()-1]);
				to[i].pop_back();
				--j;
			}
		}
		int now=0;
		while (1) {
			// cout<<"i: "<<++now<<endl;
			while (q.size() && find(e[q.top()].fir)==find(e[q.top()].sec)) q.pop();
			if (!q.size()) break;
			sta[++top]=q.top();
			int u=find(e[q.top()].fir), v=find(e[q.top()].sec);
			if (to[u].size()<to[v].size()) swap(to[u], to[v]);
			for (auto& it:to[v]) to[u].pb(it);
			uni(v, u);
			// cout<<"uv: "<<u<<' '<<v<<endl;
			for (int j=0; j<to[u].size(); ++j)
				if (siz[u]+siz[find(to[u][j].fir)]>=val[to[u][j].sec]) {
					// cout<<siz[u]<<' '<<siz[find(to[u][j].fir)]<<' '<<val[to[u][j].sec]<<' '<<to[u][j].sec<<endl;
					q.push(to[u][j].sec);
					swap(to[u][j], to[u][to[u].size()-1]);
					to[u].pop_back();
					--j;
				}
				// else cout<<"fail: "<<siz[u]<<' '<<siz[find(to[u][j].fir)]<<' '<<val[to[u][j].sec]<<endl;
		}
		printf("%d\n", top);
		for (int i=1; i<=top; ++i) printf("%d%c", sta[i], " \n"[i==top]);
	}
}

namespace task{
	bool del[N];
	ll siz[N], val[N];
	int dsu[N], lim[N], sta[N], top;
	struct edge{ll val; int to, id, s;};
	inline bool operator < (edge a, edge b) {return a.val==b.val?a.id<b.id:a.val<b.val;}
	set<edge> out[N];
	vector<edge> tem;
	priority_queue<int, vector<int>, greater<int>> q;
	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
	inline void uni(int s, int t) {if ((s=find(s))!=(t=find(t))) siz[dsu[s]=t]+=siz[s];}
	void solve() {
		for (int i=1; i<=n; ++i) dsu[i]=i, siz[i]=a[i];
		for (int i=1; i<=m; ++i) {
			if (siz[e[i].fir]+siz[e[i].sec]>=e[i].val) q.push(i);
			else {
				// cout<<"init_edge: "<<e[i].fir<<' '<<e[i].sec<<endl;
				out[e[i].fir].insert({siz[e[i].fir]+(e[i].val-siz[e[i].fir]-siz[e[i].sec])/2, e[i].sec, i, e[i].val});
				out[e[i].sec].insert({siz[e[i].sec]+(e[i].val-siz[e[i].fir]-siz[e[i].sec])/2, e[i].fir, i, e[i].val});
			}
		}
		int now=0;
		while (1) {
			// cout<<"i: "<<++now<<endl;
			// cout<<"siz: "; for (int i=1; i<=n; ++i) cout<<siz[i]<<' '; cout<<endl;
			while (q.size() && find(e[q.top()].fir)==find(e[q.top()].sec)) q.pop();
			if (!q.size()) break;
			sta[++top]=q.top();
			int u=find(e[q.top()].fir), v=find(e[q.top()].sec);
			// cout<<"uv: "<<u<<' '<<v<<endl;
			// cout<<"siz_uv: "<<siz[u]<<' '<<siz[v]<<endl;
			// cout<<"deg: "<<out[u].size()<<' '<<out[v].size()<<endl;
			tem.clear();
			// cout<<"out of u"<<endl;
			for (auto it=out[u].begin(); it!=out[u].end(); it=out[u].erase(it)) {
				if (del[it->id]) continue;
				if (it->val<=siz[u]+siz[v]) {
					// cout<<"edge: ("<<u<<','<<find(it->to)<<") val="<<it->val<<" siz_u+v="<<siz[u]+siz[v]<<" all siz="<<siz[u]+siz[v]+siz[find(it->to)]<<" s="<<it->s<<" id="<<it->id<<endl;
					if (siz[u]+siz[v]+siz[find(it->to)]>=it->s) q.push(it->id), del[it->id]=1;
					else {
						tem.pb({siz[u]+siz[v]+(it->s-siz[u]-siz[v]-siz[find(it->to)])/2, it->to, it->id, it->s});
						if (find(it->to)!=u) out[find(it->to)].insert({siz[find(it->to)]+(it->s-siz[u]-siz[v]-siz[find(it->to)])/2, u, it->id, it->s});
					}
				}
				else break;
			}
			// cout<<"out of v"<<endl;
			for (auto it=out[v].begin(); it!=out[v].end(); it=out[v].erase(it)) {
				if (del[it->id]) continue;
				if (it->val<=siz[u]+siz[v]) {
					// cout<<"edge: ("<<v<<','<<find(it->to)<<") val="<<it->val<<" siz_u+v="<<siz[u]+siz[v]<<" all siz="<<siz[u]+siz[v]+siz[find(it->to)]<<" s="<<it->s<<" id="<<it->id<<endl;
					if (siz[u]+siz[v]+siz[find(it->to)]>=it->s) q.push(it->id), del[it->id]=1;
					else {
						tem.pb({siz[u]+siz[v]+(it->s-siz[u]-siz[v]-siz[find(it->to)])/2, it->to, it->id, it->s});
						if (find(it->to)!=v) out[find(it->to)].insert({siz[find(it->to)]+(it->s-siz[u]-siz[v]-siz[find(it->to)])/2, v, it->id, it->s});
					}
				}
				else break;
			}
			if (out[u].size()<out[v].size()) swap(out[u], out[v]);
			for (auto& it:out[v]) out[u].insert(it);
			for (auto& it:tem) out[u].insert(it);
			uni(v, u);
		}
		printf("%d\n", top);
		for (int i=1; i<=top; ++i) printf("%d%c", sta[i], " \n"[i==top]);
	}
}

signed main()
{
	freopen("earthquake.in", "r", stdin);
	freopen("earthquake.out", "w", stdout);

	n=read(); m=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	for (int i=1; i<=m; ++i) {
		e[i].fir=read(), e[i].sec=read(), e[i].val=read();
		++deg[e[i].fir], ++deg[e[i].sec];
	}
	// force::solve();
	// task2::solve();
	// task1::solve();
	// if (n<=5000) force::solve();
	// else if (m==n-1 && deg[1]==m) task1::solve();
	// else task2::solve();
	task::solve();

	return 0;
}
posted @ 2022-06-21 16:41  Administrator-09  阅读(82)  评论(1编辑  收藏  举报