比赛链接https://ac.nowcoder.com/acm/contest/89860

A - TD

题意

有m个人,其中n个人发送了TD,那么从m个人中随机挑选一个人,他发送过TD的概率是多少。

思路

直接输出undefinedfrac{n}{m}$ 即可(要注意这里不是整数除法)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n,m;cin>>n>>m;
cout<<double(n)/m<<endl;
}
signed main(){
int T = 1;
// cin>>T;
while(T--){
solve();
}
return 0;
}

B - 你好,这里是牛客竞赛

题意

给你一个链接,如果这个链接以https://ac.nowcoder.com或者ac.nowcoder.com 开头,就输出Ac

如果链接以https://www.nowcoder.com 或者www.nowcoder.com 开头,就输出Nowcoder

如果都不是,那么就输出No

思路

可以先判断前几个字符如果是https;//就删掉这部分。

然后我们读取字符串,直到遇到.com就停止。

最后对读取的字符串判断即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
string str;
cin>>str;
if(str.substr(0,8) == "https://"){
str = str.substr(8);
}
string ans = "";
for(int i = 0;i<str.size();i++){
if(i >= 3 && str.substr(i-3,3) == "com") break;
ans += str[i];
}
if(ans == "ac.nowcoder.com") cout<<"Ac\n";
else if(ans == "www.nowcoder.com") cout<<"Nowcoder\n";
else cout<<"No\n";
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

C - 逆序数

题意

已知一个**排列**undefined{ x_1 ,x_2,…,x_n \}k\{x_n,…,x_2,x_1\}$ 的逆序对数量。

现在给你 ,请你输出结果。

思路

一个长度为n的排列,一共有undefinedfrac{n*(n-1)}{2}t,kans = t = \frac{n*(n-1)}{2} - k$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n,k;
cin>>n>>k;
cout<<n*(n-1)/2 - k;

}
signed main(){
int T = 1;
// cin>>T;
while(T--){
solve();
}
return 0;
}

D - 构造mex

题意

给你整数,请你将分为**恰好**n个非负整数, 使得 并且

注:的含义是**在a数组中,未出现过的最小的非负整数**。

思路

大讨论题,对于一般情况,我们很容易得知,需要构造 这样的数列。但是本题的关键在于考虑特殊情况。下面列举需要特判的条件:

  1. 如果 并且 ,条件无法成立那(这n个数中一定存在0,就不能使得k = 0.)
  2. 如果 ,那么无论如何都不能成立。(a数组一定为一个1和一些0(可能为0个0),最后的mex一定为0或2,不是1)
  3. 如果 那么无法成立(显然无法分出k份也就无法使得都在中出现,也就无法使得最终的mex是k)
  4. 如果 ,无法成立(如果n和k相等,就代表应该恰好将s分为 这k个数,如果总和不是s就违背了构造规则)
  5. 如果 并且 那么无法成立(即我们需要设置a数组为 , 那么它的mex就变为了 而不是)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int s,n,k;
cin>>s>>n>>k;
int sum = k*(k-1)/2;
if(k == 0){
if(s < n) {cout<<"NO\n";return ;}
cout<<"YES\n";
rep(i,1,n-1){
cout<<1<<' ';
}cout<<s-n+1;
puts("");return ;
}
if(k == 1 && s == 1){cout<<"NO\n";return ;}
if(n < k) {cout<<"NO\n"; return ;}
if(sum > s) {cout<<"NO\n"; return ;}
if(n == k) {
if(sum != s) {cout<<"NO\n"; return ;}
else {
cout<<"YES\n";
rep(i,0,n-1){
cout<<i<<' ';
}puts("");return ;
}
}
if(n == (k+1) && sum+k == s) {cout<<"NO\n";return ;}
cout<<"YES\n";
for(int i = 0;i<k;i++){
cout<<i<<' ';
}
int left = s-sum;
if(left == k){
cout<<1<<" "<<k-1<<' ';
for(int i = k+3;i<=n;i++) cout<<"0 ";
}else{
cout<<left<<' ';
for(int i =k+2;i<=n;i++) cout<<"0 ";
}
puts("");return ;
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

E - 小红的X型矩阵

题意

给你一个 的01矩阵, 你进行若干次如下操作,使得最终的矩阵为X型矩阵 ,操作方法如下:

  • 操作一:将矩阵中的一个元素反转(0变为1,1变为0)
  • 操作二:将矩阵循环右移或者循环下移一位。

问最少需要几次操作一,能够使得矩阵变为X型矩阵

注:当且仅当一个矩阵的两个对角线全为1,其余地方全为0时,该矩阵为X型矩阵 其余全为0)

思路

我们将最终目标的X矩阵进行一些次数的循环移动后,可以发现,X的中心移动到了矩阵中的某个位置,而其他的1同样在它的四个角落方向(若越界则循环至矩阵另一边),因此这些1同样在两条对角线上。

1
2
3
4
5
10001           11000
01010 11000
00100 ---> 00101
01010 00010
10001 00101

因此我们对输入矩阵维护每条对角线上的1的数量,然后我们枚举中心点的每个位置,找到和原矩阵匹配程度最大的那个即可(即在这两条对角线上,原矩阵的1出现的数量最多)。

需要注意如果n为偶数,因为两条对角线不存在交点,那么处理方式和奇数略有不同。

可以发现对于左上-右下对角线,我们可以这样表示它: (对于每个,表示一条对角线)

对于左下-右上对角线,可以这样表示它:(对于每个,表示一条对角线)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
int mp[1005][1005];
int n;
int sum1[2005];
int sum2[2005];
void solve(){
cin>>n;
int sum = 0;
for(int i =1;i<=n;i++){
for(int j = 1;j<=n;j++){
cin>>mp[i][j];
if(mp[i][j]) sum ++ ;
}
}
for(int d = 0;d<=n-1;d++){
for(int i = 1;i<=n;i++){
int j = i + d;
if(j > n) j -=n;
sum1[d] += mp[i][j];
}
}
for(int d = 0;d<=n-1;d++){
for(int i = 1;i<=n;i++){
int j = d - i;
if(j < 1) j += n;
sum2[d] += mp[i][j];
}
}

int res = INF;
if(n%2 == 1){
for(int i = 1;i<=n;i++){
for(int j =1;j<=n;j++){
int cnt = sum1[(j-i+n)%n] + sum2[(i+j)%n] - mp[i][j];
int ans =(2 * n - 1 - cnt) + (sum - cnt);
res = min(res,ans);
}
}
}else{
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
int cnt = 0;
cnt += sum1[(j-i+n)%n];
int ii = i%n+1;
cnt += sum2[(ii+j)%n];
int ans =(2 * n - cnt) + (sum - cnt);
res = min(res,ans);
}
}
}
cout<<res;
}
signed main(){
int T = 1;
// cin>>T;
while(T--){
solve();
}
return 0;
}

F - 小红的数组回文值

题意

定义一个数组的回文值为:至少需要修改多少个数,能使得这个数组变为回文数组。

现在给你一个长度为的数组 ,问它的所有**子序列**的回文值的和是多少。答案对 取模

思路

我们应该先想到,一个数组的回文值,即为每对**对称的数** 不相等的数量。例: ,那么undefined1,7)(2,5)$ 对称,并且这两个对都不相同,回文值为2。

即**只有不相同的数对会产生贡献**。 那么我们枚举数组a中的每个数对undefineda_i, a_j )$, 如果他们不相等, 我们就计算在多少个子序列中,这两个数是对称的。

很容易求出对于给定的undefinedi,j)mid = j-i-1le = i-1ri = n-j$ 个数。

分布如下:...i.....j....

我们得知,选择中间的任意数不会破坏对称。那么他们有 种方案。

对于两侧,我们必须选择同样多的数,才能保证是对称的。假设我们选择了个数。

于是两侧的方案数为undefinedsum_{k=0}^{min(le,ri)} C_{le}^{k} C{ri}^kC_n^m = C_n^{n-m}\sum_{k=0}^{min(le,ri)} C_{le}^{le-k} C_{ri}^kC_{le+ri}^{le}(i,j)2^{mid} * C_{le+ri}^{le}$

注:范德蒙恒等式如下: , 解释为:有两个盒子,分别装有个球和个球 , 从中一共选择k个球() 等价于, 从第一个盒子中选择t个球,从另一个盒子中选择个球 。枚举每个 将方案数相加。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
int a[2005];
int fac[2005];
int ifac[2005];
const int mod = 1e9+7;
int qpow(int x,int n){
int ans = 1;
while(n){
if(n&1) ans = ans * x % mod;
x = x * x % mod;
n >>= 1;
}
return ans;
}
int inv(int x){
return qpow(x,mod-2);
}
int C(int n,int m){
if(m == 0 || n == m) return 1;
return fac[n] * ifac[n-m] % mod * ifac[m] % mod;
}
void solve(){
fac[0] = 1;
for(int i = 1;i<=2000;i++) fac[i] = fac[i-1] * i % mod;
for(int i = 1;i<=2000;i++) ifac[i] = inv(fac[i]);
int n;
cin>>n;
for(int i = 1;i<=n;i++){
cin>>a[i];
}
int ans = 0;
for(int i = 1;i<=n;i++){
for(int j = i+1;j<=n;j++){
int l = i-1,m = (j-i-1),r=(n-j);
int sum = 0;
int mn = min(l,r);
ans = (ans + (a[i]!=a[j])*qpow(2,m)*(C(l+r,l))%mod)%mod;
}
}
cout<<ans;
}
signed main(){
int T = 1;
// cin>>T;
while(T--){
solve();
}
return 0;
}