比赛链接https://ac.nowcoder.com/acm/contest/89860
A - TD
题意
有m个人,其中n个人发送了TD,那么从m个人中随机挑选一个人,他发送过TD的概率是多少。
思路
直接输出 n m \frac{n}{m} mn 即可(要注意这里不是整数除法)
代码
#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
就停止。
最后对读取的字符串判断即可。
代码
#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 - 逆序数
题意
已知一个排列 { x 1 , x 2 , . . . , x n } \{ x_1 ,x_2,...,x_n \} {x1,x2,...,xn} 的逆序对的数量是 k k k , 请你输出 { x n , . . . , x 2 , x 1 } \{x_n,...,x_2,x_1\} {xn,...,x2,x1} 的逆序对数量。
现在给你 n , k n,k n,k ,请你输出结果。
思路
一个长度为n的排列,一共有 n ∗ ( n − 1 ) 2 \frac{n*(n-1)}{2} 2n∗(n−1) 个对, 并且只有正序对和逆序对,我们设数量分别为 t , k t,k t,k , 那么在翻转整个排列后,显然原先的正序对会变为逆序的,逆序对会变为正序的。 那么反转后的逆序对的数量,就是原先正序对的数量。 即 a n s = t = n ∗ ( n − 1 ) 2 − k ans = t = \frac{n*(n-1)}{2} - k ans=t=2n∗(n−1)−k
代码
#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
题意
给你整数 s , k s,k s,k,请你将 s s s分为恰好n个非负整数$a_1,a_2,…,a_n ,使得 , 使得 ,使得a_1+a_2+…+a_n = s$ 并且 m e x ( a ) = k mex(a) = k mex(a)=k 。
注: m e x ( a ) mex(a) mex(a)的含义是在a数组中,未出现过的最小的非负整数。
思路
大讨论题,对于一般情况,我们很容易得知,需要构造 0 , 1 , 2 , . . . , k − 1 , s − ( k ∗ ( k − 1 ) 2 ) , 0 , 0 , 0 0,1,2,...,k-1, s-(\frac{k*(k-1)}{2}),0,0,0 0,1,2,...,k−1,s−(2k∗(k−1)),0,0,0 这样的数列。但是本题的关键在于考虑特殊情况。下面列举需要特判的条件:
- 如果 k = 0 k = 0 k=0 并且 s < n s < n s<n ,条件无法成立那(这n个数中一定存在0,就不能使得k = 0.)
- 如果 k = 1 , s = 1 k = 1,s = 1 k=1,s=1 ,那么无论如何都不能成立。(a数组一定为一个1和一些0(可能为0个0),最后的mex一定为0或2,不是1)
- 如果 n < k n < k n<k 那么无法成立(显然无法分出k份也就无法使得 [ 0 , k − 1 ] [0,k-1] [0,k−1]都在 a a a中出现,也就无法使得最终的mex是k)
- 如果 n = k n=k n=k 且 s ≠ k ∗ ( k − 1 ) 2 s \not= \frac{k*(k-1)}{2} s=2k∗(k−1) ,无法成立(如果n和k相等,就代表应该恰好将s分为 0 , 1 , 2 , . . . , k − 1 0,1,2,...,k-1 0,1,2,...,k−1 这k个数,如果总和不是s就违背了构造规则)
- 如果 n = k + 1 n = k+1 n=k+1 并且 s = k ∗ ( k − 1 ) 2 + k s = \frac{k*(k-1)}{2} + k s=2k∗(k−1)+k 那么无法成立(即我们需要设置a数组为 0 , 1 , 2 , . . . , k − 1 , k 0,1,2,...,k-1,k 0,1,2,...,k−1,k , 那么它的mex就变为了 k + 1 k+1 k+1 而不是 k k k)
代码
#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型矩阵
题意
给你一个
n
∗
n
n*n
n∗n 的01矩阵, 你进行若干次如下操作,使得最终的矩阵为X型矩阵
,操作方法如下:
- 操作一:将矩阵中的一个元素反转(0变为1,1变为0)
- 操作二:将矩阵循环右移或者循环下移一位。
问最少需要几次操作一,能够使得矩阵变为X型矩阵
注:当且仅当一个矩阵的两个对角线全为1,其余地方全为0时,该矩阵为X型矩阵
(
a
i
i
=
1
,
a
i
,
n
−
i
+
1
=
1
a_{ii}= 1,a_{i,n-i+1}= 1
aii=1,ai,n−i+1=1 其余全为0)
思路
我们将最终目标的X矩阵进行一些次数的循环移动后,可以发现,X的中心移动到了矩阵中的某个位置,而其他的1同样在它的四个角落方向(若越界则循环至矩阵另一边),因此这些1同样在两条对角线上。
10001 11000
01010 11000
00100 ---> 00101
01010 00010
10001 00101
因此我们对输入矩阵维护每条对角线上的1的数量,然后我们枚举中心点的每个位置,找到和原矩阵匹配程度最大的那个即可(即在这两条对角线上,原矩阵的1出现的数量最多)。
需要注意如果n为偶数,因为两条对角线不存在交点,那么处理方式和奇数略有不同。
可以发现对于左上-右下对角线,我们可以这样表示它: i − j ≡ d ( m o d n ) i-j \equiv d (mod\ n) i−j≡d(mod n) (对于每个 d ∈ [ 0 , n − 1 ] d\in[0,n-1] d∈[0,n−1],表示一条对角线)
对于左下-右上对角线,可以这样表示它: i + j ≡ d ( m o d n ) i+j \equiv d(mod\ n) i+j≡d(mod n)(对于每个 d ∈ [ 0 , n − 1 ] d\in[0,n-1] d∈[0,n−1],表示一条对角线)
代码
#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 - 小红的数组回文值
题意
定义一个数组的回文值为:至少需要修改多少个数,能使得这个数组变为回文数组。
现在给你一个长度为 n ( 1 ≤ n ≤ 2000 ) n(1\le n \le 2000) n(1≤n≤2000)的数组 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an ,问它的所有子序列的回文值的和是多少。答案对 1 0 9 + 7 10^9+7 109+7 取模
思路
我们应该先想到,一个数组的回文值,即为每对对称的数 不相等的数量。例: 1 , 2 , 4 , 5 , 7 1,2,4,5,7 1,2,4,5,7 ,那么 ( 1 , 7 ) (1,7) (1,7)对称, ( 2 , 5 ) (2,5) (2,5) 对称,并且这两个对都不相同,回文值为2。
即只有不相同的数对会产生贡献。 那么我们枚举数组a中的每个数对 ( a i , a j ) (a_i, a_j ) (ai,aj), 如果他们不相等, 我们就计算在多少个子序列中,这两个数是对称的。
很容易求出对于给定的 ( i , j ) (i,j) (i,j) 他们之间有 m i d = j − i − 1 mid = j-i-1 mid=j−i−1个数, 左边有 l e = i − 1 le = i-1 le=i−1 个数,右边有 r i = n − j ri = n-j ri=n−j 个数。
分布如下:...i.....j....
我们得知,选择 i , j i,j i,j中间的任意数不会破坏 i i i和 j j j对称。那么他们有 2 m i d 2^{mid} 2mid 种方案。
对于两侧,我们必须选择同样多的数,才能保证 i i i和 j j j是对称的。假设我们选择了 k k k个数。$ 0 \le k \le min(le,ri)$
于是两侧的方案数为 ∑ k = 0 m i n ( l e , r i ) C l e k C r i k \sum_{k=0}^{min(le,ri)} C_{le}^{k} C{ri}^k ∑k=0min(le,ri)ClekCrik , 我们根据组合数的公式 C n m = C n n − m C_n^m = C_n^{n-m} Cnm=Cnn−m ,就可以将式子变为 ∑ k = 0 m i n ( l e , r i ) C l e l e − k C r i k \sum_{k=0}^{min(le,ri)} C_{le}^{le-k} C_{ri}^k ∑k=0min(le,ri)Clele−kCrik ,那么根据范德蒙恒等式 , 方案数即为 C l e + r i l e C_{le+ri}^{le} Cle+rile , 因此对于这个数对 ( i , j ) (i,j) (i,j) ,它的贡献为 2 m i d ∗ C l e + r i l e 2^{mid} * C_{le+ri}^{le} 2mid∗Cle+rile
注:范德蒙恒等式如下: C n + m k = ∑ t = 0 k C n t C m k − t C_{n+m}^k = \sum_{t=0}^{k} C_n^t C_m^{k-t} Cn+mk=∑t=0kCntCmk−t , 解释为:有两个盒子,分别装有 n n n个球和 m m m个球 , 从中一共选择k个球( C n + m k C_{n+m}^k Cn+mk) 等价于, 从第一个盒子中选择t个球 C n t C_n^t Cnt,从另一个盒子中选择 k − t k-t k−t个球 C m k − t C_m^{k-t} Cmk−t 。枚举每个 t ∈ [ 0 , k ] t \in [0,k] t∈[0,k] 将方案数相加。
代码
#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;
}