CF1400:1320A(小思维)、1312C(思维)、289B(绝对值)、1201C(模拟)

1320A. Journey Planning(小思维)

题意:

给定一个数列ai。
从中找一个子序列,满足i>j, i-j=a[i]-a[j]
这样的子序列和最大为多少?

思路:

满足i-j=a[i]-a[j],即满足i-a[i]=j-a[j]
然后map记录值相同的总和。取最大。

就是一个简单的公式变形,一开始还想着暴搜,dp。。

Code:

const int N = 200010, mod = 1e9+7;
ll T, n, m, a[N];
ll sum,ans;
bool f[N];

int main(){
    
    
	Ios;
	
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	
	for(int i=1;i<=n;i++)
	{
    
    
		int x=i-a[i];
		
		mp[x]+=a[i];
		
		if(mp[x]>ans) ans=mp[x];
	}
	cout<<ans;
	
	return 0;
}

经验:

以后看到给的公式,先变形看看。
别他说啥是啥,跟着绕进去了。


1312C. Adding Powers(思维)

题意:

给定k,对于长度为n的0数组a,判断能否经过下列操作转化为数组b?
对于第 i 次操作(i 从 0 开始):
1.选择一个位置,将该位置上的值 += k^i
2.跳过该次操作。
可以在任意时刻停止操作。

思路:

也就是说:
对于目标数组中的每个数,都要能转化成k的幂次数之和。并且n个数的所需要的幂次不能重复。

所以,对于每个数,都要找到最大的k的幂次数(如果不是最大,那么对于这个数一定会有重复幂次)

遍历每个数,map记录幂次出现次数,如果一个幂次多次出现,那么就不能满足。

注意:log(x)函数对于大数有精度误差。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
int k,flag;
int p[N];

void pd(int x)
{
    
    	
	while(x)
	{
    
    
		int t;
		for(int i=0;i<=x;i++){
    
    
			if(pow(k,i)<=x) t=i;
			else break;
		}
		
		if(mp[t]){
    
    
			flag=1;return;
		}
		else mp[t]=1;
		
		x-=pow(k,t);
	}
}

signed main(){
    
    
	Ios;
	
	cin>>T;
	while(T--)
	{
    
    
		flag=0;
		cin>>n>>k;
		
		mp.clear();
		for(int i=1;i<=n;i++)
		{
    
    
			int x;cin>>x;
			if(x) pd(x);
		}
		
		if(flag) cout<<"No\n";
		else cout<<"Yes\n";
	}
	
	return 0;
}

289B. Polo the Penguin and Matrix(绝对值)

题意:

给定数k。对于n个数,每次操作可以挑一个数+k或者-k。
问,最少多少次操作,能够使得所有数相等?如果不能,输出-1。

思路:

对于一个数,加k或者减k对于其模k的值是不变的。
所以如果有两个数模k的值不同,那么这两个值无论如何操作都不能使其相等。
所以所有数模k的值都相等时,才有解。

如何得到最优解呢?
让操作次数最少,就是让|x-a1|/k+|x-a2|/k+|x-a3|/k+...+|x-an|/k = (|x-a1|+|x-a2|+|x-a3|+...+|x-an|)/k的值最少,x为最终化为的相等的数。
那么,求|x-a1|+|x-a2|+|x-a3|+...+|x-an|最小,之前见到过,x最优解为ai的中位数a[n/2+1]

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];

int main(){
    
    
	Ios;
	
	int k;
	cin>>n>>m>>k;
	
	int flag=0,cnt=0,t;
	for(int i=1;i<=n;i++){
    
    
		for(int j=1;j<=m;j++){
    
    
			cnt++;
			cin>>a[cnt];
			
			if(cnt==1) t=a[1]%k;
			else if(a[cnt]%k!=t) flag=1;
		}
	}
	
	if(flag){
    
    
		cout<<-1;return 0;
	}
	n*=m;
	
	sort(a+1,a+n+1);
	
	int x=a[n/2+1];
	
	int ans=0;
	for(int i=1;i<=n;i++)
	{
    
    
		ans+=abs(x-a[i])/k;
	}
	cout<<ans;
	
	return 0;
}

扩展:

如果每次可以选择多个数+k或者-k呢?
如果在一个二维矩阵中,每次选择一个子矩阵+k或者-k呢?


1201C. Maximum Median(模拟)

题意:

给定一个长度为n的数列,给定数k。(n为奇数)
最多有k次操作,每次操作可以选一个位置,将该位置上的数+1。
问,数列中位数最多为多少?

思路:

因为是中位数,所以只有后面的n/2+1位对中位数有影响,所以要将k次操作都放在后面。
要让后面这n/2+1个位置中的最小值尽量大,将所有数放到优先队列,每次取队首进行操作。
但是k最大1e9,所以这个方案不可行。

这个题有个特性,每次操作只加1。
所以可以从小到大慢慢往上堆比较后面的值比前面的值多多少,看能不能补上,包括前面所有数。
如果能补上,比较下一位;如果不能,看最多补多少。
补后的最小值便是答案。

Code:

signed main(){
    
    
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>b[i];
	
	sort(b+1,b+n+1);
	
	int cnt=0;
	for(int i=n/2+1;i<=n;i++){
    
    
		cnt++;
		a[cnt]=b[i];
	}
	n=cnt;
	
	int ans=0;
	for(int i=1;i<n;i++)
	{
    
    
		if(m>=i*(a[i+1]-a[i])){
    
     //开long long 
			m-=i*(a[i+1]-a[i]);
			a[i]=a[i+1];
			ans=a[i];
		}
		else
		{
    
    
			int x=m/i;
			m=0;
			ans=a[i]+x;
			break;
		}
	}
	if(m){
    
    
		int x=m/n;
		ans=a[n]+x;
	}
	cout<<ans;
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Mr_dimple/article/details/121024673