CF1322C:Instant Noodles

解析

神仙题了属于是
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
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值