CF1322C Instant Noodles(思维,哈希)
CF1322C Instant Noodles(思维,哈希)
题意
给定一个大小为 的二分图,该图右部点 有权值 。定义 表示非空的左部点集, 为 中的点相连的所有右部点权和。
求对所有的 , 的 。
思路
题意有点绕,先考虑暴力解。答案形式是若干点权和求 的形式,考虑到 的结合率,我们可以试着拆分答案 分开考虑。
大概长成这样:
现在考虑这些小 是什么,考虑到右部点是权值的提供者分析其对答案贡献,任取 ,,为右部中两点,这两点的贡献和左部点的连接情况有关。
我们设 表示右部点 所连接的左部点集。
当 ,当 时有, 。所有可以合并成一个点。
当 ,就会有 $ { u } { u ,v} { u,v,u + v }$ 等情况。因为 取到了所有情况,最后对答案的贡献合并为 ,有个经典结论 。该情况下 对答案的贡献就是 。
当 ,不可能出现 的情况。贡献仍然是 。
综上,我们发现仅有第一种情况需要合并两点权,否则贡献仍然是对单点求 。
因此我们仅要做集合查重,再求这些集合的 即可。这个操作可以通过对集合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保险些。
分类:
Codeforces
, 杂题
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通