A题
题目链接
https://codeforces.com/contest/1513/problem/A
题意
给两个整数n和k,表示有一个从1到n的序列{an},要求这个序列中有k个峰,峰的意思是存在ai在序列中且ai>ai-1&&ai>ai+1,ai不能在序列的边缘。
思路
模拟。先将1~n中前半部分的数排列出来,然后在每两个数之间插入一个后半部分的较大的数,这样分析发现,如果n是偶数的时候最多有n/2-1个峰,如果n是奇数最多有n/2(向下取整)个峰,如果k大于最多可能的峰的数量就输出-1,否则在前k+1个较小的数之间插入较大的数。
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n,k;
cin>>n>>k;
if(n%2==0)//n是偶数
{
if(k>n/2-1) cout<<-1<<endl;//偶数最多n/2-1个峰
else
{
int i = 1,j = n,flag = 1;
while(i<=k+1)//实现交替输出较小的数和较大的数
{
if(flag)//输出较小的数
{
printf("%d ",i++);
flag = 0;
}
else//输出较大的数
{
printf("%d ",j--);
flag = 1;
}
}
while(i<=j) printf("%d ",i++);//将剩下的数输出
puts("");
}
}
else//同偶数
{
if(k>n/2) cout<<-1<<endl;
else
{
int i = 1,j = n,flag = 1;
while(i<=k+1)
{
if(flag)
{
printf("%d ",i++);
flag = 0;
}
else
{
printf("%d ",j--);
flag = 1;
}
}
while(i<=j) printf("%d ",i++);
puts("");
}
}
}
return 0;
}
B题
链接
https://codeforces.com/contest/1513/problem/B
题意
给定n个整数a1,a2,…,an,如果任意i属于[1,n-1]有a1&a2&…&ai=ai+1&ai+2&…&an那么就说这样的数列是好的,当这个序列可以改变顺序的时候问最多有多少个好的序列,最终结果过对1e9+10取模。
思路
这一题涉及到位运算的一些性质,首先给出如下性质。
性质一: a&b<=min(a,b)
证明: 首先将a和b写成二进制形式,同时不妨设a>b。
a = a1a2…an,b=b1b2…bm,因为a>=b,所以n>=m。那么a&b的结果只需要考虑1到m位,因为后面的位给b补0所以算出来都是0。在1到m位中只有同为1时&运算结果才为1,所以&运算结果一定小于等于b,证毕。
性质二: a1&a2&…&an<=min(a1,a2,…,an)
证明: 性质一的推广,证明同理。
于是我们可以得到结论,对于一个好的序列,序列两端一定是这个序列中最小的数。那么可以判断,如果这个序列中最小的数只出现一次,或者这个序列中其它数与这个最小的数进行并运算的结果不等于这个最小的数,就不是好的序列。所以解决该题分为三步:
第一步:找最小数及其次数,设最小数为minval,出现k次。
第二步:判断,如果k小于2就直接输出0,如果k大于等于2,就将minval和数组中每个数进行并运算看结果是不是与minval相等,如果不等也输出0。
第三步:如果确实存在好的序列,通过排列组合可得到,k个最小数选两个排在两端有k*(k-1)中排法,其它n-2个数随意排列一共有(n-2)!种排法,所以答案就是k*(k-1)*(n-2)!。从这里可以看出如果k小于2的话结果算出来就是0,所以也可以不对k>2进行判断。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1e9+7;
int k,n,t;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
vector<int> p(n);
for(int i=0;i<n;i++)
{
scanf("%d",&p[i]);
}
int minval = *min_element(p.begin(),p.end()),flag = 0;
k = 0;
for(int x:p)
{
if(x==minval) k++;
if((x&minval)!=minval)
{
printf("0\n");
flag = 1;
break;
}
}
if(flag) continue;
int fact = 1;
for(int i=1;i<=n-2;i++) fact = (LL)fact*i%MOD;
int ans = (LL)k*(k-1)%MOD;
ans = (LL)ans*fact%MOD;
printf("%d\n",ans);
}
return 0;
}
C题
题目链接
https://codeforces.com/contest/1513/problem/C
题意
给一个整数n,可以进行如下操作,对n的每一位加1使其变成一个新的数,问对这个整数n实行m次这种操作之后n会变成一个几位数,结果对1e9+7取模。
思路
动态规划,设置数组dp[i]表示对10进行i次操作后结果的长度,那么就有如下结果:
d
p
[
i
]
=
{
2
if
i
<
9
3
if
i
=
9
d
p
[
i
−
9
]
+
d
p
[
i
−
10
]
if
i
>
9
dp[i] = \begin{cases} 2 &\text{if } i<9\\ 3 &\text{if } i=9\\ dp[i-9]+dp[i-10] &\text{if } i>9 \end{cases}
dp[i]=⎩⎪⎨⎪⎧23dp[i−9]+dp[i−10]if i<9if i=9if i>9
代码
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7,N = 200010;
int dp[N];
int main()
{
for(int i=0; i<9; i++)dp[i] = 2;
dp[9] = 3;
//预先算出所有值,后面直接按下表查找就可以了
for(int i=10; i<N; i++){
dp[i] = (dp[i-9] + dp[i-10])%mod;
}
int t;
cin>>t;
while(t--){
int n, m;
scanf("%d%d",&n,&m);
int ans = 0;
while(n > 0){
int x = n%10;//一位位进行运算
ans += ((m + x < 10) ? 1 : dp[m + x - 10]);
ans %= mod;
n/=10;
}
printf("%d\n",ans);
}
return 0;
}