首页 > 其他分享 >P2500 [SDOI2012]集合

P2500 [SDOI2012]集合

时间:2023-03-22 21:36:40浏览次数:39  
标签:set int P2500 SDOI2012 num 集合 id 关键点

[SDOI2012]集合

Luogu P2500 [SDOI2012]集合

题目描述

小H在学习“集合与图论”的时候遇到了一个问题,他思考了很久依然无法很好完成这个问题。于是他只好来求助你了,给出n个点m条边的带权无向图(即每条无向边上都有一个权值),有3个集合A、B、C。一开始无向图中所有点都属于A集合,有如下9种操作:

MoveA x:表示将第x个点从所在集合中删除,并加入至A集合。

MoveB x:表示将第x个点从所在集合中删除,并加入至B集合。

MoveC x:表示将第x个点从所在集合中删除,并加入至C集合。

AskAA:询问两个端点都属于A集合的所有边中最小的权值是多少。

AskAB:询问两个端点分别属于A集合和B集合的所有边中最小的权值是多少。

AskAC:询问两个端点分别属于A集合和C集合的所有边中最小的权值是多少。

AskBB:询问两个端点都属于B集合的所有边中最小的权值是多少。

AskBC:询问两个端点分别属于B集合和C集合的所有边中最小的权值是多少。

AskCC:询问两个端点都属于C集合的所有边中最小的权值是多少。

你能帮助他解决这个问题吗?

输入格式

输入的第1行有两个正整数,分别表示n和m。

在第2行至第m+1行中,每行有三个正整数,分别为u、v、w。表示这条无向边的两个端点分别为u和v(u != v),且这个边的权值为w(w<=10^9)。

第m+2行有一个正整数q,表示有q个询问。

在第m+3行至第m+q+2行中,每行的输入方式为题目描述里9种操作中的一种。

输出格式

对于所有的Ask操作输出最小的权值,如果不存在则输出“No Found!”。

样例 #1

样例输入 #1

4 3
1 2 1 
2 3 2
3 1 3
5
AskAA
AskAB
MoveB 2
AskAA
AskAB

样例输出 #1

1
No Found!
3
1

提示

数据范围

对于其中20%的数据,满足n<=50, m<=2500, q<=2500。

对于另外30%的数据,满足n<=100, m<=10000, q<=20000。

对于另外50%的数据,满足n<=100000,m<=500000,q<=100000。且无向图上任意两个点之间至多能选出3条不相交的路径。

Solution

容易发现使用一般的数据结构很难维护这类信息,因此先考虑暴力做法,然后对暴力进行优化(下述时间复杂度默认 \(n,m,q\) 同阶)。

对于前 \(50\%\) 的数据,容易想到对所有可能的询问分别建一个 set 维护满足条件的边权。具体来说,就是建 \(6\) 个 set,分别维护 \(A\leftrightarrow A\),\(A\leftrightarrow B\),\(A\leftrightarrow C\),\(B\leftrightarrow B\),\(B\leftrightarrow C\),\(C\leftrightarrow C\) 的答案,然后对于每次修改直接暴力修改影响的 set 内的边权,具体写法很简单,不再赘述。

考虑怎么优化这个暴力。一个很典的 Trick 是将所有点按照度数进行根号分治,将度数 \(\ge \sqrt{n}\) 的点称为关键点,其余称为非关键点。容易发现,关键点的数目是 \(\mathcal O(\sqrt n)\) 级别的。

  • 对于非关键点,考虑延续上面部分分的思路,由于每一个点的度数不超过 \(\sqrt n\),因此这一部分的边的数目是 \(\mathcal O(n\sqrt n)\) 级别的,所以可以同样去维护 \(6\) 个 set 来记录答案;
  • 对于关键点,可以对每一个关键点开 \(3\) 个 set,表示关键点分别到三个集合的边集。因为关键点的数目是 \(\mathcal O(\sqrt n)\) 级别的,因此这部分边的数目也是 \(\mathcal O(n\sqrt n)\) 的。

关于修改:

  • 对于非关键点,遍历所有出边 \(u\to v\),若 \(v\) 为关键点,那么修改 \(v\) 的 set;否则修改 \(6\) 个 set 的元素;
  • 对于关键点,遍历所有可到达的关键点 \(v\),修改 \(v\) 的 set(这部分实现的时候可以先把所有点的出边按照是否是关键点排序,然后所有的出边为关键点的边就会被连续访问到);
  • 两类操作时间复杂度都是 \(\mathcal O(\sqrt n\log n)\) 的。

关于询问:

  • 先在维护的 \(6\) 个 set 中查询非关键点间的答案;
  • 然后遍历当前集合中的关键点,在关键点的 set 中查询答案;
  • 第一步时间复杂度 \(\mathcal O(\log n)\),第二步时间复杂度 \(\mathcal O(\sqrt n\log n)\)。

总时间复杂度 \(\mathcal O(n\sqrt n\log n)\),常数很小,但是由于数据原因,跑的没有暴力快。

代码实现上注意一下判断 set 是否为空,否则会 RE。由于数据很水,甚至不需要 multiset

#include<bits/stdc++.h>
using namespace std;
template<class T> void read(T &x) {
	x = 0; bool flag = 0; char b = getchar();
	while (!isdigit(b)) flag = b == '-' ? 1 : 0, b = getchar();
	while (isdigit(b)) x = x * 10 + b - 48, b = getchar();
	x = flag ? -x : x;
}
template<class T, class ...Args> void read(T &x, Args &...args) {
	read(x), read(args...);
}
constexpr int _N = 5e5 + 5, _SN = 1e3 + 5, inf = 1e9;
int n, m, q;
int X[_N], Y[_N], W[_N];
int deg[_N], MID;
set<int> f[5][5], crit[_SN][5], P[5];
int id[_N];
vector<pair<int, int>> edge[_N];
unordered_map<int, int> num;
int cnt = 0;
inline bool IsCri(int x) {return deg[x] >= MID;}
void Move(int x, int tar) {
	if (!IsCri(x)) { // not critical
		for (auto e : edge[x]) {
			int v = e.first, w = e.second;
			if (IsCri(v)) {
				crit[num[v]][id[x]].erase(w);
				crit[num[v]][tar].emplace(w);
			} else {
				int tx = min(id[x], id[v]), ty = max(id[x], id[v]);
				int fx = min(tar, id[v]), fy = max(tar, id[v]);
				f[tx][ty].erase(w);
				f[fx][fy].emplace(w);
			}
		}
		id[x] = tar;
	} else { // critical
		for (auto e : edge[x]) {
			int v = e.first, w = e.second;
			if (!IsCri(v)) break;
			crit[num[v]][id[x]].erase(w);
			crit[num[v]][tar].emplace(w);
		}
		P[id[x]].erase(x);
		id[x] = tar;
		P[tar].emplace(x);
	}
}
int Query(int x, int y) {
	int res = !f[x][y].empty() ? *f[x][y].begin() : inf;
	if (!P[x].empty()) for (int u : P[x]) {
		if (crit[num[u]][y].empty()) continue;
		res = min(res, *crit[num[u]][y].begin());
	}
	if (!P[y].empty()) for (int u : P[y]) {
		if (crit[num[u]][x].empty()) continue;
		res = min(res, *crit[num[u]][x].begin());
	}
	return res;
}
signed main() {
	ios::sync_with_stdio(0); cout.tie(0);
	read(n, m);
	for (int i = 1; i <= m; ++i) {
		read(X[i], Y[i], W[i]);
		++deg[X[i]], ++deg[Y[i]];
		edge[X[i]].emplace_back(Y[i], W[i]);
		edge[Y[i]].emplace_back(X[i], W[i]);
	}
	MID = sqrt(n);
	for (int i = 1; i <= n; ++i) {
		sort(edge[i].begin(), edge[i].end(), [](const auto &A, const auto &B) {
			return IsCri(A.first) > IsCri(B.first);
		});
		if (IsCri(i)) num[i] = ++cnt;
	}
	for (int i = 1; i <= n; ++i) Move(i, 1);
	read(q);
	for (int i = 1; i <= q; ++i) {
		char opt[15]; scanf("%s", opt + 1);
		int x;
		if (opt[1] == 'M') {
			read(x);
			Move(x, opt[5] - 'A' + 1);
		} else {
			int ans = Query(opt[4] - 'A' + 1, opt[5] - 'A' + 1);
			if (ans != inf) cout << ans << '\n';
			else cout << "No Found!" << '\n';
		}
	}
}

标签:set,int,P2500,SDOI2012,num,集合,id,关键点
From: https://www.cnblogs.com/hanx16msgr/p/17245526.html

相关文章

  • JAVA~适合新手和复习~基础三(集合所有常用方法)
    Java集合框架  1Set和List的区别21.Set接口实例存储的是无序的,不重复的数据。List接口实例存储的是有序的,可以重复的元素。342.Set检索效率低下,删除和......
  • java集合相关问题
    Hashmap原理分析ConcurrentHashMap相关问题HashMap和Hashtable和HashTree和ConcurrentMap的比较HashMap和Hashtable和HashTree和ConcurrentMap的区别Vecto......
  • Map集合
    概念:一种可以自行定义检索规则的数据结构,也叫字典构成:key-value注意:null值可以作为keykey值具有唯一性HasMmap的Key其本质是数组+链表构成的红黑树点击查看代......
  • List集合
    特点有序,有重复实现类1.ArrayList是一个基于数组的集合,其扩容策略为,默认为0,添加第一个元素直接扩展到10,此后每次扩容50%。常用API点击查看代码importjava.util.Ar......
  • 集合框架
    集合框架1.List接口2.Set接口3,Map接口一、集合的体系1.Collection接口也是一种集合,特征:无序,可重复无序:没有游标可重复:这个集合当中可以有相同的数据注意:没有......
  • Hashtable 键值对集合
    usingSystem;usingSystem.Collections;namespaceHashtable_键值对集合{classProgram{staticvoidMain(string[]args){......
  • ArrayList集合对象
    1、ArrayList集合对象usingSystem;usingSystem.Collections;namespaceArrayList集合{classProgram{staticvoidMain(string[]args)......
  • List 泛型集合
     usingSystem;usingSystem.Collections.Generic;namespaceList_泛型集合{classProgram{staticvoidMain(string[]args){......
  • nodejs处理一段redis获取集合,数组的代码优化(其中包含:es6同步返回数据的处理,new Pro
    从异步,用延时来处理,改成同步获取数据获取数据主要分2步:1.从redis集合中获取数组;2.遍历数组,抓取其中字符串,解析,拼接成需要的数据,返回给前端原代码,用sleep方法,避免异步......
  • nodejs获取redis集合内容,同步方法
    可以使用redis模块来连接和操作Redis数据库。以下是使用该模块获取Redis集合内容的同步方法://引入redis模块constredis=require('redis');//创建redis客户端const......