解析
神仙题了属于是
设SiS_iSi为右侧第i个点连向的左侧点的集合
然后把所有SiS_iSi 相同的点合并成一个点(就称为新点吧),点权相加
合并后的所有点权的gcd(设为w吧)就是答案
为什么?
首先一个显然的结论是,SiS_iSi相同的点要么都被选到,要么都选不到
然后考虑任何一个左侧点的选取方案,权值肯定是一些新点的权值和
因此w也一定是这个方案权值的因子
w的合法性得以证明
然后是w的最优性
考虑假设有一个x,是比w更大的符合的答案
那么一定至少存在一个新点k,使x不是它的因子了
那么考虑当选取的集合为SkS_kSk的时候都权值和
如果这个权值和仍是x的倍数,说明至少存在一个新点k′k'k′,使Sk′S_{k'}Sk′是SkS_{k}Sk的子集,且k’的权值也不是x的倍数(这样他们加起来才可能是x的倍数)
那么把k’作为新的k,递归到考虑选取集合为Sk′S_{k'}Sk′的情况
这样集合会越来越小,直到无法找出新的k’为止,就一定能找到一种方案,使x不是该方案权值的因子
最优性得以证明
思路有了以后,代码实现就不难了
我偷懒没有用哈希,使用的是map套vector
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e5+100;
ll read() {ll x=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=x*10+(c^48);c=getchar();}return x*f;
}int n,m;
#define V vector<int>
V v[N];
map<V,ll>mp;
map<V,ll>::iterator it;
ll gcd (ll a,ll b){return b?gcd(b,a%b):a;
}
ll a[N];
int main() {
#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);
#endifint T=read();while(T--){mp.clear();ll ans=0;n=read();m=read();for(int i=1;i<=n;i++){v[i].clear();v[i].shrink_to_fit();}for(int i=1;i<=n;i++) a[i]=read();for(int i=1;i<=m;i++){int x=read(),y=read();v[y].push_back(x);}for(int i=1;i<=n;i++){if(v[i].size()==0) continue;sort(v[i].begin(),v[i].end());mp[v[i]]=mp[v[i]]+a[i];}for(it=mp.begin();it!=mp.end();it++){ll o=(*it).second;//printf("o=%lld\n",o);ans=gcd(o,ans);}printf("%lld\n",ans);}return 0;
}
/*
3
501 502 503
*/