AtCoder Beginner Contest 162(E(数学枚举gcd)F(选n/2个数 dp))

题目链接

前四题水题,就不写题解了。

来看E

E - Sum of gcd of Tuples (Hard)

题意:

AtCoder Beginner Contest 162(E(数学枚举gcd)F(选n/2个数 dp))_第1张图片

给你n和k  要你求所有的序列组合中 gcd之和。

总共有K^n中序列

 

做法:一个很明显的就是枚举gcd,然后计算答案,

gcd=1 =>方案:k^n

gcd=2 => (k/2)^n

去掉重复计算的:

1.gcd=1->gcd1-gcd2-gcd3....

2.gcd=2->gcd2-gcd4-gcd6...

3.gcd=3>gcd3-gcd6-gcd9...

但是从1枚举到k 要考虑容斥.

倒着枚举就不需要了

 

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const ll mod=1e9+7;
ll powmod(ll a,ll b) {ll res=1;a%=mod;
assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
const int N=1e5+10;
ll n,k;
ll dp[N];
void add(ll &x,ll y)
{
    x=(x+y)%mod;
}
int main()
{

    cin>>n>>k;

    ll ans=0;
    for(ll gc=k;gc;--gc)
    {
        ll di=k/gc;
        ll num=powmod(di,n)%mod;
        ll t=0;
        for(ll j=gc+gc;j<=k;j+=gc) add(t,dp[j]);
    
        dp[gc]=(num-t+mod)%mod;
    }

    for(int i=1;i<=k;++i){
        add(ans,dp[i]*i%mod);
    }
    cout<

 

F - Select Half

AtCoder Beginner Contest 162(E(数学枚举gcd)F(选n/2个数 dp))_第2张图片

题意:给你n长度的序列a[i] 现在你要选择n/2(向下取整) 个a[i] 并且不能取相邻的数,使得所选的数尽量最大。

由于n有1e5 不能开二维的dp

做法:做法参考别人的代码

大概就是dp[i]维护前dp[i]个合法的最大值吧。

每次当前位置i  选x个  要从x-1的位置转移 结合不能相邻的特性选一下吧

 

#include 
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
ll dp[N][2],a[N];
void solve()
{
	int n;
	cin >> n;

	for (int i = 1; i < n + 1; i++)
		cin >> a[i];
	memset(dp, 0, sizeof(dp));
	dp[2][1] = a[2];
	dp[2][0] = a[1];
	for (int i = 3; i < n + 1; i++)
	{
		if (i & 1)
		{
			ll temp = max({dp[i-3][1],dp[i-3][0],dp[i-2][1],dp[i-2][0]});
			//位置i选择
			//i:x个
			//i-1 x个
			//i-2:x-1个
			//i-3:x-1个
			//i-4: x-2个
			dp[i][1] = a[i] + temp;

			dp[i][0]=max({dp[i-1][1],dp[i-1][0],a[i-2]+dp[i-3][0]});
			//arr[i-3]+dp[i-2][1] :不合法
			//该位置不选,从x继承dp[i-1]
			//i-1:x个
			//i-2:x-1个
			//i-3: x-1个
		}
		else
		{
			dp[i][1]=a[i]+max({dp[i-2][1],dp[i-2][0],dp[i-1][0]});
			//选当前位置 x=i/2
			//i-1:x-1个
			//i-2:x-1个


			//位置i不选,从x位置转移 由于没有x位置
			//i-1:x-1个
			//i-2 x-1个
			//i-3:x-2个
			//结论:一定选i-1  维护前i个必须选i/2个的特性
			dp[i][0] = a[i-1]+dp[i-2][0];
		}
	}
	cout << max(dp[n][1], dp[n][0]) << endl;
	return;
}

int main()
{
	solve();
	return 0;
}
/*
没有两个相邻的被选上
8
1 2 3 4 -1 -2 -3 -4
*/

 

你可能感兴趣的:(AtCode题解)