1606C. Banknotes、1604B(思维),1604C(思维)

1606C. Banknotes

题意:

定义 f ( x ) f(x) f(x) 为数 x x x 能被 1 0 a i 10^{a_i} 10ai 表示所用的最小个数
例如: a i = a_i= ai={ 0 , 1 , 2 0,1,2 0,1,2 } 时, f ( 59 ) = 14 。 ( 59 = 5 ∗ 1 0 1 + 9 ∗ 1 0 0 , 5 + 9 = 14 f(59) = 14。(59=5*10^1 + 9*10^0,5+9=14 f(59)=1459=5101+91005+9=14)
给定一个数 k k k,求数 x x x最小值使得 f ( x ) > k f(x)>k f(x)>k

思路:

f ( x ) > k f(x)>k f(x)>k,也就是说 f ( x ) = k + 1 f(x)=k+1 f(x)=k+1
为了让数 x x x 尽量小,就要疯狂消耗前面较小的 1 0 a i 10^{a_i} 10ai。就让前面的 1 0 a i 10^{a_i} 10ai的系数尽量大。
但是不能太大,因为如果太大的话, f ( x ) f(x) f(x) 就要用后面较大的 1 0 a i 10^{a_i} 10ai表示了。

那对于一个ai,当前 1 0 a i 10^{a_i} 10ai 的系数最大为多少呢?
例如:
a i = a_i = ai={ 0 , 1 , 3 0,1,3 0,1,3},那么 1 0 a i = 10^{a_i}= 10ai={ 1 , 10 , 1000 1,10,1000 1,10,1000}。

  • 对于第一个数1,其最大贡献值为9,构成的值为9,因为如果是10的话, f ( x ) f(x) f(x) 就要用10来表示,就不用1了。
  • 对于第二个数10,其最大贡献值为99,构成的值为990,因为如果是1000的话, f ( x ) f(x) f(x)就要用1000来表示,就不用10了。

所以对于 a i a_i ai,其最大贡献值 cnt 为 1 0 a i + 1 / 1 0 a i − 1 10^{a_{i+1}}/ 10^{a_i}-1 10ai+1/10ai1,能够构成的值为 c n t ∗ 1 0 a i cnt*10^{a_i} cnt10ai
x 减去该值,继续下一位置。
如果 x 比 c n t ∗ 1 0 a i cnt*10^{a_i} cnt10ai 小,那么就让 cnt 为 x。
如果遍历完所有位置最后有剩余,全部用最后一个位置来贡献。

Code:

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

signed main(){
    
    
	Ios;
	
	cin>>T;
	while(T--)
	{
    
    
		cin>>n>>m;
		for(int i=1;i<=n;i++) cin>>a[i];
		
		int ans=0;
		m++;
		for(int i=1;i<n;i++)
		{
    
    
			int x=pow(10,a[i]),y=pow(10,a[i+1]);
			int t=y/x-1;
			
			if(t<=m){
    
    
				ans+=t*x;
				m-=t;
			}
			else{
    
    
				ans+=m*x;
				m=0;
				break;
			}
		}
		if(m) ans+=m*pow(10,a[n]);
		
		cout<<ans<<endl;
	}
	
	return 0;
}

B. XOR Specia-LIS-t(思维)

题意:

给定一个数列 {a}。
可以将该数列分割为若干个子数列,每个子数列得到一个 b i b_i bi 值:为当前子数列中的最长上升子序列的长度。
问,这个 {b} 数组的总异或值能否为0?

思路:

当时看到“最长上升子序列”,完全就慌掉了。。心里还想,这tm才是B题!!

用到异或的一个性质:偶数个相同的数异或值为0
bi 为当前子数列最长上升子序列的长度:

  • 如果数列长度为偶数,我们可以将整个数列分割为n份,每个子数列中只有一个数,那么bi值便都是1,总的异或值为0。
  • 否则,长度为奇数。偶数好考虑,所以要尽量往偶数上靠。把当前的奇数长度转为偶数。遍历一遍数组,如果存在相邻的两个位置,后面的数比前面的数大,那么可以将这两个位置分到同一个数组,贡献值仍为1。其他都挨个分割。
    这样,就有偶数个bi,每个都是1,总的异或值为0。

这样分奇偶判断一下就好。

比赛时还在想,最长上升子序列怎么处理。。sb了。

Code:

#define Ios ios::sync_with_stdi
const int N = 200010, mod = 1e9 + 7;
int T, n, m, a[N];

int main(){
    
    
	Ios;
	
	cin>>T;
	while(T--)
	{
    
    
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		
		if(n%2==0) cout<<"Yes\n";
		else
		{
    
    
			bool flag=0;
			for(int i=1;i<n;i++){
    
    
				if(a[i+1]<=a[i]) flag=1;
			}
			if(flag) cout<<"Yes\n";
			else cout<<"No\n";
		}
	}
	
	return 0;
}

1604C. Di-visible Confusion(思维)

题意:

给定一个数组,可以进行若干次下述操作,问最终数组长度能否变为0?
操作:选择一个位置 i,如果满足 a[i] % (i+1) 不为0,就可以将该位置删掉。后面的位置往前递补。

思路:

从前往后遍历每个位置 i,判断2~i+1位置,是否存在位置 j,使得 a[i] % (j+1) 不为0。
如果存在,说明这个位置能删得去,遍历下一位置。
否则,当前位置删不去,数组长度不能变为0,break。

疑问1:如果对于两个位置x,y (x<y),y满足的位置 j 在 x 满足的位置 k 前面怎么办?
因为是从前往后遍历,所以遍历到后面位置时,前面的位置可以删掉了。前面的位置都可以删,所以 后面 y 的位置可以任意确定。

疑问2:两重循环不超时?
第二重循环在满足条件时就会及时break掉,而从前往后找到一个非当前数的因数是很快的,所以总的复杂度不高。
官方题解说明,第二重循环最多循环不超过22次。

Code:

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

int main(){
    
    
	Ios;
	
	cin>>T;
	while(T--)
	{
    
    
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		
		bool ff=0;
		for(int i=1;i<=n;i++)
		{
    
    
			bool flag=0;
			for(int j=2;j<=i+1;j++)
			{
    
    
				if(a[i]%j!=0){
    
    
					flag=1;break;
				}
			}
			if(flag) continue;
			else{
    
    
				ff=1;break;
			}
		}
		if(ff) cout<<"No\n";
		else cout<<"Yes\n";
	}
	
	return 0;
}

反思:

当时比赛的时候,这两道题都被卡了。原因是什么?当时看到题的时候,一下子没有思路,就慌掉了,总觉得这题有多复杂多复杂,一直没想怎么去解决,不敢想!
加上有队友在旁边。。

这两道题有个特点,都是判断题,判断所给数据能否有解。看来这样的题要发散思路,不能用常规的思路去想,要大胆想!大胆假设,小心求证!

おすすめ

転載: blog.csdn.net/Mr_dimple/article/details/121068956