2024CCPC网络赛题目(一)包含L,K,B,I
2024CCPC网络预选赛题解
2024CCPC网络赛题目(一)包含L,K,B,I
所有题目链接
文章目录
L 网络预选赛
思路
签到题,输入数据后,暴力枚举即可
解题代码
// L
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int ans=0;
char g[510][510];
int main()
{
ios::sync_with_stdio(false) ;
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
{
if(g[i-1][j]=='c'&&g[i-1][j]==g[i-1][j+1]&&g[i-1][j+1]==g[i][j+1]&&g[i][j]=='p')
ans++;
}
cout<<ans;
return 0;
}
K 取沙子游戏
思路
首先遍历一定范围内n值,比如140时,k从1n的获胜情况,通过观察,寻找规律。再使用规律来完成题解。
首先可以确定的时当,n为奇数时,Alice必赢,因为当k=1时,有奇数个1,代表了Alice赢。所以只用看n取偶数时的情况
探索代码
遍历所有情况的代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int lowbit(int x){
return x&-x;
}
//探索思路
int dfs(int n,vector<int>& v,int p,int cnt,vector<int> path)
{
//cnt1表示乙选择
//cnt0表示甲选择
if(n==0)
{
// cout<<"数量"<<path.size()<<"\n";
// for(int i=0;i<path.size();i++)
// cout<<path[i]<<"->";
int anc = path.size()%2;
// cout<<"\n";
// if(anc)
// cout<<"甲";
// else
// cout<<"乙";
// cout<<"\n" ;
//返回的是获胜情况 在该路情况下 1甲赢 0乙赢
return anc;
}
for(int i=0;i<=p;i++)
if(n-v[i]<0) continue;
else
{
vector<int> path2=path;
path2.push_back(v[i]) ;
int m =dfs(n-v[i],v,i,1-cnt,path2);
//如果返回的是自己赢,那肯定会选自己
if(m==cnt)
return cnt;
}
//都没有找到能让自己赢的方法,就返回对手赢
return 1- cnt;
}
int main()
{
int n,k;
for(int z=2;z<100;z+=2)
{
n=z;
k=z;
bool find = false;
cout<<"n:"<<n<<"\n" ;
for(int i=1;i<k;i++)
{
vector<int> v;
vector<int>path;
// cout<<"公因子:";
for(int j=1;j<=i;j++) if(i%j==0) v.push_back(j);
// for(int k=0;k<v.size();k++) cout<<v[k]<<" ";
// cout<<"\n";
path.push_back(i);
string anss = dfs(n-i,v,v.size()-1,0,path)==1?"甲":"乙";
// cout<<"dfs:"<<anss<<" "<<"\n";
if(anss.compare("甲")==0)
{
bitset<8> binary_rep(n); // 假设是8位二进制数,根据实际情况调整位数
cout<<"最低位为"<<(lowbit(n))<<"\n";
cout << binary_rep << "\n";
cout<<"k="<<i<<"时"<<" ";
cout<<anss<<"赢\n";
cout<<"\n" ;
cout<<(lowbit(n))<<" "<<i<<"\n" ;
if(lowbit(n)==i)
{
cout<<"yes\n";
}
else
{
cout<<"no\n";
}
find = true;
break;
}
}
if(!find)
{
cout<<"乙赢了" <<"\n\n" ;
}
}
}
这是前30的情况
n:2
乙赢了
n:4
乙赢了
n:6
最低位为2
00000110
k=2时 甲赢
2 2
yes
n:8
乙赢了
n:10
最低位为2
00001010
k=2时 甲赢
2 2
yes
n:12
最低位为4
00001100
k=4时 甲赢
4 4
yes
n:14
最低位为2
00001110
k=2时 甲赢
2 2
yes
n:16
乙赢了
n:18
最低位为2
00010010
k=2时 甲赢
2 2
yes
n:20
最低位为4
00010100
k=4时 甲赢
4 4
yes
n:22
最低位为2
00010110
k=2时 甲赢
2 2
yes
n:24
最低位为8
00011000
k=8时 甲赢
8 8
yes
n:26
最低位为2
00011010
k=2时 甲赢
2 2
yes
n:28
最低位为4
00011100
k=4时 甲赢
4 4
yes
n:30
最低位为2
00011110
k=2时 甲赢
观察以上代码时,发现规律,当k为偶数时,只要k至少大于等于自己二进制的最低位时,甲可以获胜。
一个数的二进制最低位的含义
比如6,6的二进制是110,那么它最低为也就是10,也就是2
比如7,7的二进制是111,那么它最低为就是1,也就是1
解题方程
A l i c e 必赢 { 当 n 为奇数时, 当 n 为偶数时且 l o w b i t ( n ) < = k 时, Alice必赢\begin{cases} 当n为奇数时,\\ 当n为偶数时且lowbit(n)<=k时, \end{cases} Alice必赢{当n为奇数时,当n为偶数时且lowbit(n)<=k时,
解题代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
//返回一个数最低为的函数
int lowbit(int x){
return x&-x;
}
//Problem K. 取沙子游戏
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)
{
int n,k;
cin>>n>>k;
bool F=false;
if(n&1)
{
F=true;
}
else
{
if(lowbit(n)<=k)
{
F=true;
}
}
if(F)
{
cout<<"Alice"<<"\n";
}
else
cout<<"Bob"<<"\n";
}
}
B 军训 Ⅱ
思路
思路,为了取得最小的身高极差,显然当将序列降序或升序时,即可。
9 9 8 2 4 4 3 5 3
就会变为
2 3 3 4 4 5 8 9 9
由于同一身高的人,可以随意调换位置,所以应该将每个身高的排序情况相乘(An取n,也就是A的阶乘)得到答案。
∏
i
=
1
n
!
a
i
,
其中
a
i
表示身高为
i
的人数
\prod_{i = 1}^{n}!a_i,其中a_i表示身高为i的人数
i=1∏n!ai,其中ai表示身高为i的人数
解题方程
考虑到降序和升序也是两种不同的情况,但只对于有多组同身高的情况,所以有以下分类
{
2
∗
!
∏
i
=
1
n
!
a
i
,当有多组不同情况身高时
!
a
i
,当只有一组身高的情况时
\begin{cases} 2*!\prod_{i = 1}^{n}!a_i,当有多组不同情况身高时\\ !a_i,当只有一组身高的情况时 \end{cases}
{2∗!∏i=1n!ai,当有多组不同情况身高时!ai,当只有一组身高的情况时
解题代码
//B
#include<bits/stdc++.h>
#define int long long
using namespace std;
using ll = long long;
const int mod = 998244353;
unordered_map<int,int> mcap;
int Am_n(int m,int n)
{
int ans=1;
int mm = m;
while(n--)
{
if(mcap.count(m))
{
//为了避免溢出,需要取模
ans = (ans*mcap[m])%mod;
break;
}
ans=(ans*m--)%mod;
}
mcap[m] = ans;
// cout<<ans<<"\n";
return ans;
}
signed main()
{
int n;
ios::sync_with_stdio(false) ;
cin.tie(0);
cout.tie(0);
cin>>n;
vector<int> a(n);
for(int i=0;i<n;i++) cin>>a[i];
sort(a.begin(),a.end());
int ans=0;
for(int i=0;i<n-1;i++)
for(int j=i+1;j<n;j++)
ans+=a[j]-a[i];
//统计连续的个数
int cnts=1;
int cnt=1;
vector<int> m;
for(int i=1;i<n;i++)
{
if(a[i]==a[i-1]) cnt++;
else
{
m.push_back(cnt);
// cout<<a[i-1]<<" "<<cnt<<"\n";
cnt=1;
cnts++;
}
}
m.push_back(cnt);
int anss=1;
for(int i=0;i<m.size();i++)
{
int h =Am_n(m[i],m[i]);
// cout<<"个数"<<h<<"\n";
anss= (anss*h)%mod;
// cout<<anss<<"\n";
}
if(cnts>1)
anss = (anss+anss)%mod; //为了避免溢出,需要取模
cout<<ans<<" "<<anss<<"\n";
}
I 找行李
思路(参考官方题解)
官方题解思路:
有一个很好的性质,先将人和行李排序,如果第 i 个人选了行李 j,则第 i 个人往后的人选 j 以前的行
李,对答案一定是没有贡献的,则有个朴素dp:fi,j,k 表示前 i 个人,最后一个被选的行李是 j,在 j 之
前有 k 个行李还没有被选,则每次转移枚举选哪个行李即可(或者不选)。时间复杂度 O(n4)。
但是并不好优化,换一个思路:令 g(d) 表示答案大于等于 d 的方案数,则答案为 ∑∞
d=1 g(d),因为一个
方案的答案为 x,则会被 g(1) 到 g(x) 分别统计一次。
考虑求一个 g(d),每个人可以选的行李个数是确定的,就是左边距离他大于等于 d 的行李们,且第 i 个
人能选的行李集合一定是第 i + 1 个人的子集,则可以dp:fi,j 表示前 i 个人选 j 个行李的方案数,转移
只需要考虑选和不选。时间复杂度 O(n3)。
理解思路
就是,我们首先将人的位置进行排序,并将处于行李同一位置的数量进行统计。
d=n的意思,可以理解为过了ns后,行李与人可以产生配对的情况。
观看一遍这个很好的性质:[有一个很好的性质,先将人和行李排序,如果第 i 个人选了行李 j,则第 i 个人往后的人选 j 以前的行
李]再来理解下面的语言
假设过了
d
s
,判断某个人能够选择哪些行李,显然是根据
b
i
(
第
i
个人
)
的位置,以及过了的时间
d
s
,来决定的。
满足条件
:
b
i
−
d
>
=
a
i
(
表示第
i
个物品
)
,
该行李可以被这个人选择
为了快速得到哪些行李,是满足上面条件的,可以使用统计过后的行李数量,再对其求前缀和的方式,来快速拿到满足条件的所有行李数量
f
[
i
]
表示的是,在有
i
−
1
个人的情况下,能有多少种匹配情况
d
p
[
i
]
表示的是,在有
i
个人的情况下,能有多少种匹配情况
假设过了ds,判断某个人能够选择哪些行李,显然是根据b_i (第i个人)的位置,以及过了的时间ds,来决定的。\\ 满足条件:b_i-d>=a_i(表示第i个物品),该行李可以被这个人选择\\ 为了快速得到哪些行李,是满足上面条件的,可以使用统计过后的行李数量,再对其求前缀和的方式,来快速拿到满足条件的所有行李数量\\ f[i]表示的是,在有i-1个人的情况下,能有多少种匹配情况\\ dp[i]表示的是,在有i个人的情况下,能有多少种匹配情况
假设过了ds,判断某个人能够选择哪些行李,显然是根据bi(第i个人)的位置,以及过了的时间ds,来决定的。满足条件:bi−d>=ai(表示第i个物品),该行李可以被这个人选择为了快速得到哪些行李,是满足上面条件的,可以使用统计过后的行李数量,再对其求前缀和的方式,来快速拿到满足条件的所有行李数量f[i]表示的是,在有i−1个人的情况下,能有多少种匹配情况dp[i]表示的是,在有i个人的情况下,能有多少种匹配情况
假设:过了ds
遍历
b
i
假设
b
1
有
n
个满足条件的行李
,
那么它只能影响一个人匹配行李的情况,因为目前只有它一个人正在做选择,
所以它只能对
d
p
[
1
]
造成影响
,
所以
d
p
[
1
]
+
=
n
∗
f
[
0
]
,
含义就是在先前情况
(
f
[
0
]
)
选择的基础上,进行选择
更新
f
,
变为更新后的
d
p
遍历
b
i
+
1
假设
b
2
有
m
个满足条件的行李
,
那么它会影响一个人匹配行李的情况以及两个人匹配行李的情况,
所以它不仅会对
d
p
[
1
]
造成影响还会对
d
p
[
2
]
造成影响
d
p
[
1
]
+
=
m
∗
f
[
0
]
,
影响一个人匹配行李的情况
d
p
[
2
]
+
=
(
m
−
1
)
∗
f
[
0
]
影响两个人个人匹配行李的情况【在有一个人选择的情况下,显然可选择的行李数量少了
1
个】
遍历b_i\\ 假设b_1有n个满足条件的行李,那么它只能影响一个人匹配行李的情况,因为目前只有它一个人正在做选择,\\ 所以它只能对dp[1]造成影响,所以dp[1]+=n*f[0],含义就是在先前情况(f[0])选择的基础上,进行选择\\ 更新f,变为更新后的dp\\ 遍历b_{i+1}\\ 假设b_2有m个满足条件的行李,那么它会影响一个人匹配行李的情况以及两个人匹配行李的情况,\\ 所以它不仅会对dp[1]造成影响还会对dp[2]造成影响\\dp[1]+=m*f[0],影响一个人匹配行李的情况\\dp[2]+=(m-1)*f[0]影响两个人个人匹配行李的情况【在有一个人选择的情况下,显然可选择的行李数量少了1个】
遍历bi假设b1有n个满足条件的行李,那么它只能影响一个人匹配行李的情况,因为目前只有它一个人正在做选择,所以它只能对dp[1]造成影响,所以dp[1]+=n∗f[0],含义就是在先前情况(f[0])选择的基础上,进行选择更新f,变为更新后的dp遍历bi+1假设b2有m个满足条件的行李,那么它会影响一个人匹配行李的情况以及两个人匹配行李的情况,所以它不仅会对dp[1]造成影响还会对dp[2]造成影响dp[1]+=m∗f[0],影响一个人匹配行李的情况dp[2]+=(m−1)∗f[0]影响两个人个人匹配行李的情况【在有一个人选择的情况下,显然可选择的行李数量少了1个】
同时最大匹配情况为min(可匹配行李的数量,人数)
将所有满足匹配条件的b_I匹配过后,就得到在该时刻,满足匹配情况的所有情况数量。
假设经过累加求和过后的数量,为
c
n
t
[
]
举个例子,比如有行李数据
1
,
1
,
3
,
3
,
5
那么正常的话
c
n
t
[
]
=
[
0
,
2
,
0
,
2
,
0
,
1
]
经过累加求和过后
c
n
t
[
]
=
[
0
,
2
,
2
,
4
,
4
,
5
]
假设经过累加求和过后的数量,为cnt[]\\ 举个例子,比如 有行李 数据 1,1,3,3,5 那么正常的话cnt[]=[0,2,0,2,0,1] 经过累加求和过后 cnt[]=[0,2,2,4,4,5]
假设经过累加求和过后的数量,为cnt[]举个例子,比如有行李数据1,1,3,3,5那么正常的话cnt[]=[0,2,0,2,0,1]经过累加求和过后cnt[]=[0,2,2,4,4,5]
解题方程
k = m i n ( s i z e ( b ) , c n t [ b i − d ] ) − − 表示人最大能匹配行李数量的匹配数 ∑ d = 1 505 ( ∑ i s i z e ( b ) 当 ( b i − d > = 0 时 ( ∑ j = k 1 d p [ i ] + = f [ i − 1 ] ∗ j ) ) , k = min(size(b),cnt[b_i-d])--表示人最大能匹配行李数量的匹配数 \\\sum_{d=1}^{505}(\sum_{i}^{size(b)}当(bi-d>=0时(\sum_{j=k}^{1}dp[i]+=f[i-1]*j)), k=min(size(b),cnt[bi−d])−−表示人最大能匹配行李数量的匹配数d=1∑505(i∑size(b)当(bi−d>=0时(j=k∑1dp[i]+=f[i−1]∗j)),
解题代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
vector<int>a(510);
vector<int>an(510);
const int lim = 998244353;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
vector<int>b(m);
//对处于某一位置的行李进行统计
for(int i=1;i<=n;i++)
{
cin>>a[i];
++an[a[i]];
}
//进行累计前缀和
for(int i=1;i<=500;i++) an[i] +=an[i-1];
//对人的位置进行升序
for(int i=0;i<m;i++)cin>>b[i];
sort(b.begin(),b.end()) ;
int ans=0;
//将d从1~510开始遍历
for(int d=1;d<510;d++)
{
//用于维护的dp数组,表示了前n个人匹配行李的情况
vector<int> dp(m+1,0);
//初始化//0个人选择行李时,有一种情况【便于后续计算】
dp[0] =1;
//p有b的数量轮次,显然第n轮最多能选到n人,
for(auto p:b)
{
//用于记录前一轮的人的选择情况
auto ddp = dp;
//求取过了该时间ds后,该人可选择的行李的情况
int num = (p-d>=0?an[p-d]:0);
if(num!=0)
{
// std::cout<<"当前num:"<<num<<"\n";
}
//显然最多 只可能出现在前n轮人选择最多不超过m的情况下。(最大就是一人匹配一个行李)
for(int i=1;i<min(num,m)+1;i++)
{
//
dp[i] =(dp[i] + ddp[i-1]*(num - i + 1))%lim;
// cout<<"dpk:"<<i<<" "<<dp[i]<<" "<<"\n" ;
}
}
bool first =false;
for(auto c:dp)
{
if(!first)
{
first = true;
continue;
}
//对过了s后,满足条件的所有匹配情况进行统计
ans=(ans+c)%lim;
}
// cout<<"\n";
}
cout<<ans<<"\n";
}
更多推荐
所有评论(0)