M-value-The 2019 ICPC Asia-East Continent Final

题目链接(牛客网复现赛)

**知识点:按底数分集合处理+二进制枚举

大佬的解析

/*
选择第i个,选择第j个,如果i^k==j;那么就要减去bj
但是可以发现,对于2来说,3不会形成上面的影响;
但是4会对2产生影响;
2:2,4,8,16,32……
3:3,9,27…
-----
把这些数分成若干集合:
但是所选的数x>sqrt(n)的时候
有一部分x对后面的数不产生影响,要把这部分数筛出来
然后:
依次处理每个集合:
采用二进制枚举的方式,对每个集合进行处理
*/ 

/*
二进制枚举 
*/
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int num=1e5+10;
LL a[num],b[num],c[num];
int db[num];
int n; 
bool vis[num];
bool judge(int s,int t)
{
	for(LL i=s*s;i<=t;i*=s)
	{
		if(i==t) return 1;
	}
	return 0;
}
LL solve(int cnt)//二进制枚举答案
{
	LL mx=0;
	for(int i=0;i<(1<<cnt);i++)
	{
		//对于每一个i,我找到对应的数据集合;
		//然后对找到的数据集合进行处理
		int tot=0;LL temp=0;
		for(int j=1;j<=cnt;j++)//判断哪一个值应该在i对应的集合中; 
		{
			if((i>>(j-1))&1){
				c[++tot]=db[j];temp+=a[db[j]];
			} 
		}

		for(int s=1;s<=tot;s++)
		{
			for(int t=1;t<=tot;t++)
			{
				if(c[s]<c[t]&&judge(c[s],c[t]))
				{
					temp-=b[c[t]];
				}
			}
		}
		mx=max(temp,mx); 
	}
	return mx;
} 
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
	
	LL ans=a[1];
	for(LL i=2;i<=n;i++){
		if(!vis[i]){
			for(LL k=i*i;k<=n;k*=i){
				vis[k]=1;
			}
		}
	}

	for(int i=2;i<=n;i++)
	{
		if(i*i<=n&&!vis[i]){
			int cnt=0;
			for(LL k=i;k<=n;k*=i) db[++cnt]=k;
			ans+=solve(cnt);
		}
		else if(i*i>n&&!vis[i]){
			ans+=a[i];
		}
	}
	
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(二进制)