• 【Henu ACM Round#17 F】Upgrading Array


    【链接】 我是链接,点我呀:)
    【题意】

    在这里输入题意

    【题解】

    如果我们对某一个位置i操作两次的话。 显然结果就和操作一次一样。 因为第一次操作过后1..i这些数字就变成是互质的了。 gcd为1.那么除过之后没有影响的。

    然后。就是要明白
    那个f(x)函数的意义。其实就是问你x质因数分解之后,其中好的质数和坏的质数的差是多少。
    也即有多少个好因数,多少个坏因数。

    (以下的gcd(i)都指的是a[1..i]这些数字的gcd
    然后考虑我们在第i个位置进行了一次操作。
    显然他会对后面的数字造成影响。
    比如在第i个位置进行了一次操作。
    那么我们在第i+1个位置进行操作的时候,就会发现gcd(i+1)此时已经不是之前的数字。会变成1.
    这样我们可能就会漏解。
    因为gcd(i)>=gcd(i+1)
    我们可能除得太多了。
    使得我们下一次不能除少一点了。

    但是如果我们倒过来做的话,就会不一样。
    比如我们先在i进行了一次操作。
    之后再到i-1进行一次操作的话。
    gcd(i-1)不一定就等于1;
    因为原先gcd(i-1)>=gcd(i)
    所以在i进行的操作的时候。
    我们下一次到某个小于i的位置的时候,
    只需记录一下上一次已经除掉了多少。
    这次再加的时候去掉上次的影响就好了。
    但是假设我们在i,j,k(i<j<k)这3个位置都进行了操作。
    我们只需要记录gcd(j)就好。
    因为gcd(k)肯定包含在gcd(j)中了。
    然后在j位置的影响再累加一下就是gcd(k)了;

    然后在i位置的影响就是gcd(i)/gcd(k)了;
    也就是说,我们在算gcd(i)的好素数和坏素数差的时候,只需要把它当成gcd(i)/gcd(k)算就好了。
    (前提是之前已经选过k这个位置了。

    根据上面的分析我们可以用动态规划来解决这个问题了。

    需要维护两个量,1个是当前的位置,另外就是上一次进行操作的位置。

    每个位置i有两种选择:
    1.不进行操作,则上一次进行操作的位置不变。
    2.进行一次操作,则上一次进行操作的位置变成i.

    设f(x)表示x的美丽值。
    dp[i][j]表示前i个位置,上一次选择的位置是j的最大美丽值。

    (dp[i][i] = max(dp[i][i],dp[i+1][j]+(gcd[j]-gcd[i])*i; 第i个位置进行一次操作)
    (dp[i][j] = max(dp[i][j],dp[i+1][j]);第i个位置不进行一次操作)
    初值dp[n+1][n+1]等于(∑f(a[i]))
    (即不进行任何操作
    (这里n+1就表示之前没有进行任何一次操作的情况。
    (根据上面的讨论,不难想到这个dp应该倒序做即i倒序。但是j的话随意吧。
    最后取max(dp[i][j])就是答案了。

    【代码】

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    
    const int N = 5e3;
    
    int n, m, a[N + 10],_gcd[N+10];
    int dp[N + 10][N + 10];//前i个数字,上一次操作的位置在j的最大美丽值。
    set<int> myset;
    map<int, int> dic;
    
    int gcd(int x, int y) {
    	if (y == 0)
    		return x;
    	else
    		return gcd(y, x%y);
    }
    
    int f(int x) {
        if (x <= 1) return dic[x] = 0;
    	if (dic.find(x) != dic.end()) return dic[x];
    	int temp = x,point = 0;
    	for (int i = 2; i*i <= temp; i++)
    		if (temp%i == 0) {
    			int flag = 0;
    			if (myset.find(i) != myset.end()) {//找到了
    				flag = -1;
    			}
    			else {
    				flag = 1;
    			}
    			while (temp%i == 0) {
    				temp /= i;
    				point += flag;
    			}
    		}
    
    	if (temp > 1) {
    		if (myset.find(temp) != myset.end())
    			point--;
    		else
    			point++;
    	}
    	return dic[x] = point;
    }
    
    int main() {
    	#ifdef LOCAL_DEFINE
    		freopen("rush_in.txt", "r", stdin);
    	#endif
    	ios::sync_with_stdio(0), cin.tie(0);
    	cin >> n >> m;
    	for (int i = 1; i <= n; i++) cin >> a[i];
    	_gcd[1] = a[1];
    	for (int i=2; i <= n; i++) _gcd[i] = gcd(_gcd[i - 1], a[i]);
    
    	for (int i = 1; i <= m; i++) {
    		int x;
    		cin >> x;
    		myset.insert(x);
    	}
    
    
    	memset(dp, -0x3f3f3f3f, sizeof dp);
    
    	dp[n + 1][n + 1] = 0;
    	for (int i = 1; i <= n; i++) dp[n+1][n + 1] += f(a[i]);
    	for (int i = n;i >= 1;i--)
    		for (int j = n + 1; j > i; j--) {
    			//在i这个位置放一个
    			dp[i][i] = max(dp[i][i], dp[i + 1][j] +f(_gcd[j])*i- f(_gcd[i]) * i);
    			//i这个位置不放
    			dp[i][j] = max(dp[i][j], dp[i + 1][j]);
    		}
    
    
    	int ans = dp[n + 1][n + 1];
    	for (int i = 1; i <= n; i++)
    		for (int j = i; j <= n; j++)
    			ans = max(ans, dp[i][j]);
    	cout << ans << endl;
    	return 0;
    }
    
    
  • 相关阅读:
    noip模拟赛 梦想
    noip模拟赛 水题
    noip模拟赛 猜数字
    Java基础知识强化64:基本类型包装类的引入
    Java基础知识强化63:Arrays工具类之方法源码解析
    Java基础知识强化62:Arrays工具类之概述和使用
    Java基础知识强化61:经典查找之 常见查找算法小结
    Java基础知识强化60:经典查找之二分查找
    Java基础知识强化59:String(字符串)和其他类型的相互转化
    Java基础知识强化58:经典排序之二叉树排序(BinaryTreeSort)
  • 原文地址:https://www.cnblogs.com/AWCXV/p/8430071.html
Copyright © 2020-2023  润新知