The 2024 ICPC Latin America Championship F

文章讨论了如何利用裴蜀定理和根号优化技巧,解决一个关于整数组合的问题,目标是判断是否存在特定条件下所有整数的线性组合等于某个定值。通过计算最大公约数和转化成多重背包问题,使用二进制优化求解dp数组。
摘要由CSDN通过智能技术生成

Problem - F - Codeforces

该式子可以转化为:

\sum_{i\in st}^{}xi*Ri+\sum_{i\in st}^{}Gi = \sum_{i\notin st}^{}xi*Ri+\sum_{i\notin st}^{}Gi

不妨转化为

\sum_{i}^{}xi*Ri+ = sum-2*\sum_{i\in st}^{}Gi

当右边为一个定值的时候,即为讨论方程解的存在性问题

根据 n个整数间的裴蜀定理:

设a1,a2,a3......an为n个整数,d是它们的最大公约数,那么存在整数x1......xn使得x1*a1+x2*a2+...xn*an=d。

特别来说,如果a1...an存在任意两个数是互质的(不必满足两两互质),那么存在整数x1......xn使得x1*a1+x2*a2+...xn*an=1。证法类似两个数的情况。

我们得出:

 gcd(R1,R2,R3...) | sum-2*\sum_{i\in st}^{}Gi

只要对于任意一个G的某种组合,它是gcd的一个倍数,则为Y

下面我们考虑如何找出G的所有组合

题目中给出了\sum Gi <=2e5,我们不妨考虑根号优化,至多有不超过根号n种G[i],我们统计每种G[i]的数量,对于每一种Gi,这是一个多重背包问题,我们不妨采用二进制优化。我们采用一个dp数组来记录每一种状态是否可以,对于一个贡献为w的物品,dp|=dp<<w 

对于dp[0],当且仅当存在0时,我们才可以选取一个st==0的集合,因为我们显然不能将所有的图纸都分给同一个人。

#define  _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<algorithm> 
#include<string.h>
#include<iostream>
#include<queue>
#include<math.h>
#include<string>
#include<map>
#include<functional>
#include<unordered_map>
#include<bitset>
#include<set>

using namespace std;

#define int long long
#define inf 0x3f3f3f3f
#define N 200005


struct point
{
	int g;
	int r;

};

point ps[N];


int gcd(int da, int xiao)
{
	if (da < xiao)
	{
		swap(da, xiao);
	}


	int temp;
	while (xiao != 0)
	{
		temp = da%xiao;
		da = xiao;
		xiao = temp;
	}
	return da;
}


int xx[N];

bitset<N>bt;


signed main(void)
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	int n;

	cin >> n;

	if (n == 1)
	{
		cout << "N" << '\n';
	}
	else
	{
		int sum = 0;

		int tmp = -inf;

		for (int i = 1; i <= n; i++)
		{
			cin >> ps[i].g >> ps[i].r;

			sum += ps[i].g;

			if (tmp == -inf)
			{
				tmp = ps[i].r;
			}
			else
			{
				tmp = gcd(tmp, ps[i].r);
			}

			xx[ps[i].g]++;
		}

		
		bt[0]=1;
		for (int i = 1; i <N; i++)
		{
			if (!xx[i])continue;

			vector<int>vc;

			for (int j = 1; xx[i]; j *= 2)
			{
				if (xx[i]>=j)
				{
					vc.push_back(j);

					xx[i] -= j;
				}
				else
				{
					vc.push_back(xx[i]);
					break;
				}
			}

			for (int j = 0; j < vc.size(); j++)
			{
				bt |= bt << (vc[j] * i);
			}

			bt[0] = (xx[0]>0);
		}


		int ans = 0;

		for (int i = 0; i < sum; i++)
		{
			if (bt[i] && ((sum - i * 2) % tmp == 0))
			{
				ans = 1;
				break;
			}
		}



		if (ans)
		{
			cout << "Y" << '\n';
		}
		else
		{
			cout << "N" << '\n';
		}

	}






	system("pause");
	return 0;
}

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值