Divide by Zero 2021 and Codeforces Round #714 (Div. 2)

A. Array and Peaks

传送门

题意

给你一个n表示的是这个数组的长度,并且数组的元素只能有[1,n]范围内唯一的数确定,然后给你一个k表示的是你构造的数组的高峰数目,高峰指的是 中间元素比两边元素大eg: 1 3 2 ,3就是这个数组的一个高峰

解题思路

很明显我们会发现高峰数是有一个取值范围的[0,(n-1)/2],在这个范围内是我们能取到的正确高峰,我们不难发现,我们要想构造完最多的高峰我们用到的作为高峰数一定是后面的(n-1)/2个数,那么我们就可以先输出一个当前元素,然后再输出一个高峰元素,这样构造即可,不懂请看代码

Code

#include<bits/stdc++.h>
using namespace std;

int t,n,k;

int main()
{
	cin>>t;
	while(t--) {
		cin>>n>>k;
		if(k > (n-1)/2) {
			puts("-1");
			continue;
		}
		int cnt = k;
		for(int i = 1,j = n;i <= n-k; ++i) {
			printf("%d ",i);
			if(cnt)
				printf("%d ",j),cnt--,j--;
		}
		puts("");
	}
	
	
	return 0;
}

B. AND Sequences

传送门

题意

给你一个长度为n的数串,问你在这个数串的全排列中有多少中情况满足 i从1开始,取到n-1,使得a1&a2&……ai=ai+1 &ai+2 & an

解题思路

很明显这是一道组合数学的问题,然后从题目给的样例来看我们能知道我们求得结果是不需要去重的(去重难写得多)

通过题目的这种&操作我们可以得到一下几个结论

1.我们构造的数串最左边和最右边一定是最小的值,注意这里不一定是初始串的最小数

2.任意几个不相等的数&起来不大于这几个数的最小值,eg 2 & 1 = 0

3.我们计算的组合数不需要去重

这样我们先扫一遍数组,所有数&起来,得到一个最小值,看这个数的个数m如果小于2的话,那么显然构造不了,否则的话就可以直接用组合数m*(m-1)* A(n-m,n-m),注意对1e9+7取模

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long

const ll mod = 1000000007;

ll t,n;

const int N = 200005;

map<ll,ll> vis;
ll a[N];


int main()
{
	scanf("%lld",&t);
	while(t--) {
		scanf("%lld",&n);
		vis.clear();
		scanf("%lld",&a[1]);
		ll min_k = a[1];
		vis[a[1]]++;
		for(int i = 2;i <= n; ++i) {
			scanf("%lld",&a[i]);
			min_k &= a[i];
			vis[a[i]]++;
		}
		if(vis[min_k] < 2) {
			puts("0");
			continue;
		}
		ll ans = (vis[min_k] % mod *(vis[min_k] - 1) % mod ) % mod;
		ll len = n - 2; 
		for(int i = 2;i <= len; ++i) {
			ans = (ans % mod * i % mod) % mod;
		}
		printf("%lld\n",ans);
	}
	
	return 0;
}

C. Add One

题意

T组数据,每行数据输入一个n表示的是初始数的大小,然后输入一个m表示的是我们要操作的次数,每次操作我们将当前这个数的每个元素加一,eg:89 -> 910

十进制加法,但是每次操作只是针对某一位的

解题思路

很明显我们会想到一个在线操作的方法,也就是对每一个n和m以O(m)的时间复杂度,首先统计初始数在[0,9]范围内的个数 ,然后我们每次操作只用更新[0,9]范围内的元素的个数变化即可,然鹅,这题卡的很死,要T,因为题目把t的时间也算进去了的,所以我们可以预处理一下,我们先算出数字9操作2e5次后数字的长度变为多少,经过这样的处理,我们得到答案的复杂度可以降到 O(t * log10 (n)),预处理的时间复杂度为O(10 * m)

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long 


const int mod = 1000000007;
const int N = 200005;
int t,n,m;

int dp[N][10];//dp[i][j]表示的是从数字9操作i次后数字j的个数
int len[N];//len[i]表示的是数字9操作i次后的长度

int main()
{
	dp[0][9] = 1;//初始化最开始有我们假设的一个数9
	for(int i = 1;i <= 200000; ++i) {
		for(int j = 1;j <= 10; ++j) {
			dp[i][j] = dp[i-1][j-1];//这里就是我们刚才提到的状态转移,都是从上次的位置转移过来的
		}
		dp[i][0] = dp[i - 1][9];
		dp[i][1] = (dp[i][1] + dp[i -1][9]) % mod;//9 -> 10所以9会转移到0,1这两个位置
		for(int j = 0;j < 10; ++j) {
			len[i] = (len[i] + dp[i][j]) % mod;//这里就是计算每次操作后当前长度的总和
		}
		
	}
	scanf("%d",&t);
	while(t--) {
		scanf("%d%d",&n,&m);
		ll ans = 0;
		while(n) {
			int loc = n % 10;
			n /= 10;
			if(loc + m < 10) //这里就是只有一位数的长度
				ans = (ans + 1) % mod;
			else
				ans = (ans + len[loc + m - 9]) % mod;
		}
		printf("%lld\n",ans);
	}

	return 0;
} 
posted @ 2021-04-25 09:43  MangataTS  阅读(84)  评论(0编辑  收藏  举报