解析
神仙题了属于是
设
S
i
S_i
Si为右侧第i个点连向的左侧点的集合
然后把所有
S
i
S_i
Si 相同的点合并成一个点(就称为新点吧),点权相加
合并后的所有点权的gcd(设为w吧)就是答案
为什么?
首先一个显然的结论是,
S
i
S_i
Si相同的点要么都被选到,要么都选不到
然后考虑任何一个左侧点的选取方案,权值肯定是一些新点的权值和
因此w也一定是这个方案权值的因子
w的合法性得以证明
然后是w的最优性
考虑假设有一个x,是比w更大的符合的答案
那么一定至少存在一个新点k,使x不是它的因子了
那么考虑当选取的集合为
S
k
S_k
Sk的时候都权值和
如果这个权值和仍是x的倍数,说明至少存在一个新点
k
′
k'
k′,使
S
k
′
S_{k'}
Sk′是
S
k
S_{k}
Sk的子集,且k’的权值也不是x的倍数(这样他们加起来才可能是x的倍数)
那么把k’作为新的k,递归到考虑选取集合为
S
k
′
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);
#endif
int 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
*/