CF1322C Instant Noodles 题解
CF1322C Instant Noodles
- 有 \(t\) 组测试数据。
- 给出一张点数为 \(2N\) 的二分图,其中右侧的第 \(i\) 个点有点权为 \(c_i\)。
- 令 \(S\) 表示左侧点的一个非空点集,设 \(f(S)\) 表示右侧点中至少与 \(S\) 中一个点相连的点的点权和。
- 请你求出,对于所有非空集合 \(S\),\(f(S)\) 的 \(\gcd\) 为多少。
- \(1 \leq t,\sum n,\sum m \leq 5\times 10^5\),\(1 \leq c_i \leq 10^{12}\)。
显然不能枚举每个集合,我们从 $\gcd $ 的众多角度思考这个问题。
显然 \(\gcd(a,b)=\gcd(a,b,a+b)\)。
所以令 \(S_u\) 为与右侧点 \(u\) 点相邻的左侧点的集合。
如果 \(S_u \cap S_v=\varnothing\),那么答案为 \(\gcd(c_u,c_v)\)。
如果 \(S_u=S_v\),那么这个集合的和显然为 \(c_u+c_v\)。
如果 \(S_u\cap S_v\neq \varnothing\) 时,结果为 \(\gcd(c_u,c_u+c_v)\) 或 \(\gcd(c_v,c_u+c_v)\) 或 \(\gcd(c_u,c_v,c_u+c_v)\)。其最终的结果均为 \(\gcd(c_u,c_v)\)。(三种情况分别对应两种包含和一种只相交一部分)
所以我们只需要判断两个右侧的点的集合是否相等即可。
这个东西可以哈希,也可以直接使用 map+vector。
const int N=500005;
int n,m;ll a[N];
vector<int>lis[N];
map<vector<int>,ll>val;
inline void cfmode(){
val.clear();
n=read(),m=read();
for(int i=1;i<=n;++i){
a[i]=read();
lis[i].clear();
}
for(int i=1;i<=m;++i){
int u=read(),v=read();
lis[v].push_back(u);
}
for(int i=1;i<=n;++i){
sort(lis[i].begin(),lis[i].end());
if(lis[i].size())val[lis[i]]+=a[i];//size!
}
ll ans=0;
for(auto it:val)ans=__gcd(ans,it.second);
printf("%lld\n",ans);
}
int main(){
int T=read();
while(T--)cfmode();
return 0;
}

浙公网安备 33010602011771号