CF1322C Instant Noodles(思维,哈希)

CF1322C Instant Noodles(思维,哈希)

题意

给定一个大小为 2n 的二分图,该图右部点 i 有权值 ci 。定义 S 表示非空的左部点集,f(S)S 中的点相连的所有右部点权和。

求对所有的 Sf(S)gcd

思路

题意有点绕,先考虑暴力解。答案形式是若干点权和求 gcd 的形式,考虑到 gcd 的结合率,我们可以试着拆分答案 gcd 分开考虑。

大概长成这样: gcd(......)=gcd(gcd(..),gcd(..),gcd(..)...)

现在考虑这些小 gcd 是什么,考虑到右部点是权值的提供者分析其对答案贡献,任取 uv,为右部中两点,这两点的贡献和左部点的连接情况有关。

我们设 T(i) 表示右部点 i 所连接的左部点集。

T(u)=T(v) ,当 uS 时有, vS 。所有可以合并成一个点。

T(u)T(v) ,就会有 $ { u } { u ,v} { u,v,u + v }$ 等情况。因为 S 取到了所有情况,最后对答案的贡献合并为 gcd(u,v,u+v) ,有个经典结论 gcd(u,v)=gcd(u,v,u+v) 。该情况下 u,v 对答案的贡献就是 gcd(u,v)

T(u)T(v)= ,不可能出现 u+v 的情况。贡献仍然是 gcd(u,v)

综上,我们发现仅有第一种情况需要合并两点权,否则贡献仍然是对单点求 gcd

因此我们仅要做集合查重,再求这些集合的 gcd 即可。这个操作可以通过对集合hash完成。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
const int MAXN =10 + 2e5 ,mod=1e9 + 9;
// Hash
const int mod1 = 1e9 + 7;
const int mod2 = 1e9 + 9;
typedef pair<int,int> Hash;
Hash B,pw[MAXN];
Hash operator+ (Hash lv,Hash rv){
	int x = lv.first + rv.first, y = lv.second + rv.second;
	if(x >= mod1) x += mod1;
	if(y >= mod2) y += mod2;
	return make_pair(x,y);
}
Hash operator- (Hash lv,Hash rv){
	int x = lv.first - rv.first, y = lv.second - rv.second;
	if(x < 0) x += mod1;
	if(y < 0) y += mod2;
	return make_pair(x,y);
}
Hash operator* (Hash lv,Hash rv){
	return make_pair(lv.first * rv.first % mod1, lv.second * rv.second % mod2);
}
void hashInit() {
	B = Hash(rnd(mod1), rnd(mod2));
	pw[0] = Hash(1,1);
	for(int i = 1;i < MAXN;i ++) pw[i] = pw[i - 1] * B;
}
// --Hash-END--
void solve()
{    
	int n,m; cin >> n >> m;
	vector<int> c(n + 1);
	rep(i,1,n) cin >> c[i];
	vector<vector<int>> s(n + 1);
	rep(i,1,m) {
		int u,v; cin >> u >> v;
		// v : right  need to get S(v)
		s[v].push_back(u);
	}
	// set hash
	map<Hash,int> mp;
	rep(i,1,n) {
		if(s[i].empty()) continue;
		Hash hv = Hash(0,0);
		sort(s[i].begin(),s[i].end());
		for(auto x : s[i]) {
			hv = hv * B + Hash(x,x);
		}
		mp[hv] += c[i];
	}
	int ans = 0;
	for(auto [h,sc] : mp) ans = ans ? gcd(ans,sc) : sc;
	cout << ans << endl;
}
signed main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

	hashInit();
	int T;cin>>T;
	while(T--)
		solve();

	return 0;
}

记得排序保证点集有序,另外单hash也可以过,双hash保险些。

posted @   Mxrurush  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示