题意:
给你一个二分图,求左侧端点的所有可能子集中的点相连的右侧端点的权值的和的最大公因数。
题解:
若所有右侧端点均不在同一左侧子集中,则求所有权值的最大公因数即可 。
否则,将在相同左侧子集中的右侧权值合并,求合并权值与其余权值的最大公因数。
证明 :
$gcd(a,a+b)=gcd(a,b),gcd(a,b,c)=gcd(a,gcd(b,c)) 。$
Tips:
Time Limit | 2s |
ios::sync_with_stdio(false) + cin.tie(nullptr) + cout.tie(nullptr) + & | 1965ms |
scanf | 1622ms |
scanf + & | 655ms |
简直是降维打击
#include <bits/stdc++.h> using namespace std; typedef long long ll; vector<pair<vector<int>,ll>> v; bool cmp(pair<vector<int>,ll> & a,pair<vector<int>,ll> & b){//对每个右端点所在的左侧子集排序 if(a.first.size()!=b.first.size()) return a.first.size()<b.first.size(); else return a.first<b.first; } void solve(){ int n,m;scanf("%d%d",&n,&m); v.clear(); v.resize(n); for(int i=0;i<n;i++) scanf("%lld",&v[i].second); for(int i=0;i<m;i++){ int a,b;scanf("%d%d",&a,&b); --a,--b; v[b].first.push_back(a); } for(int i=0;i<n;i++) sort(v[i].first.begin(),v[i].first.end()); sort(v.begin(),v.end(),cmp); ll ans=0; int i=0; while(v[i].first.empty()) i++;//跳过空子集 while(i<n){ int j=i; ll sum=0; while(j<n&&(v[i].first==v[j].first)){//合并权值 sum+=v[j].second; ++j; } ans=__gcd(ans,sum); i=j; } printf("%lld ",ans); } int main(){ int t;scanf("%d",&t); while(t--) solve(); return 0; }