AtCoder Beginner Contest 333
A
题意
输出n个n(n<=9)
代码
#include<bits/stdc++.h>
using namespace std;
void solve(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cout<<n;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;//cin>>T__;
while(T__--)solve();
return 0;
}
B
题意
给定一个正五边形ABCDE,现在给定两条边问两条边是否相等
思路
在正五边形里面边长分为两类,一类是相邻的节点连边(如AB,BC,CD,DE,EA),另一类是不相邻的节点连边(如AC,AD,BD,BE,CE)如果两条边处在同一类中则边长相等
代码
#include<bits/stdc++.h>
using namespace std;
void solve(){
string line1,line2;
cin>>line1>>line2;
int len1=abs(line1[1]-line1[0]);
int len2=abs(line2[1]-line2[0]);
if(len1==len2)cout<<"Yes\n";
else if((len1+len2)%5==0)cout<<"Yes\n";
else cout<<"No\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;//cin>>T__;
while(T__--)solve();
return 0;
}
C
题意
一个集合A中有{1,11,111,1111,…},需要在集合A中选出三个数(可以相同)相加后组成一个新的数x,将x放入集合B中,集合B内部按照从小到大的顺序排列,问集合中第N小的元素是什么
思路
直接暴力三层循环选出三个元素相加后放入数组中,然后对整个数组排序即可
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
vector<int>res;
for(int i=1;i<=1e18;i=10*i+1){
for(int j=i;j<=1e18;j=10*j+1){
for(int k=j;k<=1e18;k=10*k+1){
res.push_back(i+j+k);
}
}
}
sort(res.begin(),res.end());
int x;
cin>>x;
cout<<res[x-1]<<"\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;//cin>>T__;
while(T__--)solve();
return 0;
}
D
题意
给定一棵树,每次可以删除一个叶子节点及其相连边,问至少需要删多少次才能删除节点1
思路
如果1是叶子节点,那么可以直接1步删除
考虑整棵树以1为根,如果要使得1号节点被删除,就意味着要删到1号节点为叶子节点的时候,那么对于1号节点的所有儿子节点为根的子树来说,这些子树最多只能保留一个,剩余的子树上的节点需要全部删除,考虑到删除节点要最小,所以我们保留1号节点的儿子节点中最大的那一棵子树即可。求子树大小简单写一个dfs即可
(如图:子树1>子树3>子树4>子树2,那么最优方案 一定是把子树1保留,剩下所有子树上的节点全部删除)
代码
#include<bits/stdc++.h>
using namespace std;
const int mxn=1e6+5;
int siz[mxn];
vector<int>edge[mxn];
void dfs(int u,int fa){
siz[u]=1;
for(auto v:edge[u]){
if(v==fa)continue;
dfs(v,u);
siz[u]+=siz[v];
}
}
void solve(){
int n;
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs(1,0);
int res=n-1;
for(auto v:edge[1])res=min(res,n-siz[v]);
cout<<res<<"\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;//cin>>T__;
while(T__--)solve();
return 0;
}
E
题意
Takahashi在冒险中遇到N个事件,每个事件由(op,x)组成,如果op=1说明这个地方有一瓶类型为x的药水,如果op=2说明遇到怪物并且需要用x类型药水才能打败他,否则会被打败
问在满足不被怪物打败的前提下,需要用背包装药水,问背包的容量最小为多少
思路
考虑贪心,每瓶药在打怪之前最后出现位置才会拿。那么我们只需要从最后往前找即可
代码
#include<bits/stdc++.h>
using namespace std;
void solve(){
int n;
cin>>n;
vector<pair<int,int>>a(n+10);
for(int i=1;i<=n;i++)cin>>a[i].first>>a[i].second;
vector<int>now(n+10,0);//在这之前需要取的药
vector<int>vis(n+10,0);//是否取
for(int i=n;i>=1;i--){
if(a[i].first==2)now[a[i].second]++;
else{
if(now[a[i].second]>=1)now[a[i].second]--,vis[i]=1;
}
}
for(int i=1;i<=n;i++){
if(now[i]>0){
cout<<"-1\n";
return;
}
}
int maxv=0,nowv=0;//最大容量
for(int i=1;i<=n;i++){
if(a[i].first==1)nowv+=vis[i];
else nowv--;
maxv=max(maxv,nowv);
}
cout<<maxv<<"\n";
for(int i=1;i<=n;i++){
if(a[i].first==1)cout<<vis[i]<<" ";
}
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;//cin>>T__;
while(T__--)solve();
return 0;
}
F
题意
有序号为1,2…n号人在排队, 每一次对队首的人进行操作,队首的人有1/2的概率出队,还有1/2的概率回到队尾,问第i个人是最后一个留在队列中的概率
思路
消元类概率 dp 经典问题
我们发现实际上无论队伍里面的人序号是什么,我们只关心当前队列中有多少个人,最后胜出的是排在第几个位置上的人
先考虑我们当前知道什么?我们知道如果队列中只有一个人,那么他必胜
再考虑我们需要求什么?在有n个人的队伍中每个位置上的人获胜的概率
所以我们现在已知最后的结束状态值,我们想求初始状态的值
因为操作可逆,所以我们可以把题意改成每一次有1/2的概率在队首加上一个人,有1/2的概率将队尾元素放到队首。那么我们就可以很顺其自然的推状态
令 d p [ i ] [ j ] dp[i][j] dp[i][j]为当前中有i个人,其中第j个人最后胜利的概率
那么就有转移方程 d p [ i ] [ j ] = 1 2 × d p [ i − 1 ] [ j − 1 ] + 1 2 × d p [ i ] [ j − 1 ] dp[i][j]=\frac {1}{2}\times dp[i-1][j-1]+\frac {1}{2}\times dp[i][j-1] dp[i][j]=21×dp[i−1][j−1]+21×dp[i][j−1]
(当前状态可以是原先状态在队首加上一个人,也可以是将队尾的人放到队首得到的)
但我们会发现这样转移有一个位置不对,如果我们当前要求的是队伍中第一个人获胜的概率(即求dp[i][1]),那么这个人显然不可能是从原先第0个人转移过来的(dp[i-1][j-1]=dp[i-1][0]=0),而是从队尾移到队首的(即是从dp[i][i])转移过来的(在实际的意义中我们其实也不难发现队首的这个人出队以后就不会对后续队列中的人产生影响)
所以最终的转移方程应该为
d
p
[
i
]
[
j
]
=
{
1
2
×
d
p
[
i
−
1
]
[
j
−
1
]
+
1
2
×
d
p
[
i
]
[
j
−
1
]
(
j
≠
1
)
1
2
×
d
p
[
i
]
[
i
]
(
j
=
1
)
dp[i][j]= \begin{cases} \frac {1}{2}\times dp[i-1][j-1]+\frac {1}{2}\times dp[i][j-1]&(j\neq 1) \\ \frac {1}{2}\times dp[i][i]&(j=1) \end{cases}
dp[i][j]={21×dp[i−1][j−1]+21×dp[i][j−1]21×dp[i][i](j=1)(j=1)
状态转移方程的确定仅仅是开始
我们意识到这个转移方程不满足无后效性的递推条件
因为在转移中dp[i][1]需要用到dp[i][i]的状态,dp[i][2]要用到dp[i][1]的状态…dp[i][i]要用到dp[i][i-1]的状态,那么很成功的一下子整层状态都锁住了
但我们发现成环的部分i都相同,也就是说是在同一层的状态,我们能否假定一个当前未知的状态的值把环上所有状态表示出来?
答案是肯定的
假定dp[i][1]是已知条件,则有
{
d
p
[
i
]
[
1
]
=
d
p
[
i
]
[
1
]
d
p
[
i
]
[
2
]
=
d
p
[
i
−
1
]
[
1
]
2
+
d
p
[
i
]
[
1
]
2
d
p
[
i
]
[
3
]
=
d
p
[
i
−
1
]
[
2
]
2
+
d
p
[
i
]
[
2
]
2
=
d
p
[
i
−
1
]
[
2
]
2
+
d
p
[
i
−
1
]
[
1
]
2
+
d
p
[
i
]
[
1
]
2
2
=
d
p
[
i
−
1
]
[
2
]
2
+
d
p
[
i
−
1
]
[
1
]
4
+
d
p
[
i
]
[
1
]
4
.
.
.
.
d
p
[
i
]
[
i
]
=
d
p
[
i
−
1
]
[
i
−
1
]
2
+
d
p
[
i
−
1
]
[
i
−
2
]
4
+
d
p
[
i
−
1
]
[
i
−
3
]
8
+
.
.
.
.
+
d
p
[
i
−
1
]
[
1
]
2
i
−
1
+
d
p
[
i
]
[
1
]
2
i
−
1
\begin{cases} dp[i][1]=dp[i][1] \\ dp[i][2]=\frac {dp[i-1][1]}{2}+\frac {dp[i][1]}{2} \\ dp[i][3]=\frac {dp[i-1][2]}{2} +\frac {dp[i][2]}{2}=\frac {dp[i-1][2]}{2}+\frac {\frac {dp[i-1][1]}{2}+\frac {dp[i][1]}{2}}{2}=\frac {dp[i-1][2]}{2}+\frac {dp[i-1][1]}{4}+\frac {dp[i][1]}{4} \\ .... \\ dp[i][i]=\frac {dp[i-1][i-1]}{2}+\frac {dp[i-1][i-2]}{4}+\frac {dp[i-1][i-3]}{8}+....+\frac {dp[i-1][1]}{2^{i-1}}+\frac {dp[i][1]}{2^{i-1}} \end{cases}
⎩
⎨
⎧dp[i][1]=dp[i][1]dp[i][2]=2dp[i−1][1]+2dp[i][1]dp[i][3]=2dp[i−1][2]+2dp[i][2]=2dp[i−1][2]+22dp[i−1][1]+2dp[i][1]=2dp[i−1][2]+4dp[i−1][1]+4dp[i][1]....dp[i][i]=2dp[i−1][i−1]+4dp[i−1][i−2]+8dp[i−1][i−3]+....+2i−1dp[i−1][1]+2i−1dp[i][1]
又因为 d p [ i ] [ 1 ] = 1 2 × d p [ i ] [ i ] 所以就有 d p [ i ] [ 1 ] = d p [ i − 1 ] [ i − 1 ] 4 + d p [ i − 1 ] [ i − 2 ] 8 + d p [ i − 1 ] [ i − 3 ] 16 + ⋯ + d p [ i − 1 ] [ 1 ] 2 i + d p [ i ] [ 1 ] 2 i 即 d p [ i ] [ 1 ] = ∑ k = 1 i − 1 1 2 i − k + 1 × d p [ i − 1 ] [ k ] + d p [ i ] [ 1 ] 2 i 移项后得到 ( 2 i − 1 ) × d p [ i ] [ 1 ] = 2 i × ∑ k = 1 i − 1 1 2 i − k + 1 × d p [ i − 1 ] [ k ] 即 d p [ i ] [ 1 ] = 2 i 2 i − 1 × ∑ k = 1 i − 1 1 2 i − k + 1 × d p [ i − 1 ] [ k ] \begin{aligned} &\text{又因为 } dp[i][1] = \frac{1}{2} \times dp[i][i] \\ &\text{所以就有 } dp[i][1] = \frac{dp[i-1][i-1]}{4} + \frac{dp[i-1][i-2]}{8} + \frac{dp[i-1][i-3]}{16} + \cdots + \frac{dp[i-1][1]}{2^i} + \frac{dp[i][1]}{2^i} \\ &\text{即 } dp[i][1] = \sum_{k=1}^{i-1} \frac{1}{2^{i-k+1}} \times dp[i-1][k] + \frac{dp[i][1]}{2^i} \\ &\text{移项后得到 } (2^i - 1) \times dp[i][1] = 2^i \times \sum_{k=1}^{i-1} \frac{1}{2^{i-k+1}} \times dp[i-1][k] \\ &\text{即 } dp[i][1] = \frac{2^i}{2^i - 1} \times \sum_{k=1}^{i-1} \frac{1}{2^{i-k+1}} \times dp[i-1][k] \end{aligned} 又因为 dp[i][1]=21×dp[i][i]所以就有 dp[i][1]=4dp[i−1][i−1]+8dp[i−1][i−2]+16dp[i−1][i−3]+⋯+2idp[i−1][1]+2idp[i][1]即 dp[i][1]=k=1∑i−12i−k+11×dp[i−1][k]+2idp[i][1]移项后得到 (2i−1)×dp[i][1]=2i×k=1∑i−12i−k+11×dp[i−1][k]即 dp[i][1]=2i−12i×k=1∑i−12i−k+11×dp[i−1][k]
经过这么一番处理我们发现,每一层的dp[i][1]实际上只和i-1层的状态有关,所以可以直接处理出来
那么已知dp[i][1]的状态,第i层的所有状态也就可以递推处理出来了
至此我们可以将转移方程更新为:
d
p
[
i
]
[
j
]
=
{
1
2
×
d
p
[
i
−
1
]
[
j
−
1
]
+
1
2
×
d
p
[
i
]
[
j
−
1
]
(
j
≠
1
)
2
i
2
i
−
1
×
∑
k
=
1
i
−
1
1
2
i
−
k
+
1
×
d
p
[
i
−
1
]
[
k
]
dp[i][j]= \begin{cases} \frac {1}{2}\times dp[i-1][j-1]+\frac {1}{2}\times dp[i][j-1]&(j\neq 1) \\ \frac{2^{i}}{2^{i}-1}\times\sum _{k=1}^{i-1}{\frac{1}{2^{i-k+1}}\times dp[i-1][k]} \end{cases}
dp[i][j]={21×dp[i−1][j−1]+21×dp[i][j−1]2i−12i×∑k=1i−12i−k+11×dp[i−1][k](j=1)
初始已知
d
p
[
1
]
[
1
]
=
1
dp[1][1]=1
dp[1][1]=1,最后要求的是
f
[
n
]
[
1
]
,
f
[
n
]
[
2
]
,
f
[
n
]
[
3
]
.
.
.
.
.
.
f
[
n
]
[
n
]
f[n][1],f[n][2],f[n][3]......f[n][n]
f[n][1],f[n][2],f[n][3]......f[n][n]
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353,mxn=3e3+5;
int dp[mxn][mxn];
int fastpow(int a,int power=mod-2){
int res=1;
while(power){
if(power&1)res=res*a%mod;
a=a*a%mod;
power>>=1;
}
return res;
}
int inv(int x){return fastpow(x);}
void solve(){
int n;
cin>>n;
int inv2=inv(2);
dp[1][1]=1;
for(int i=2;i<=n;i++){
for(int k=1;k<=i-1;k++)dp[i][1]+=inv(fastpow(2,i-k+1))*dp[i-1][k]%mod,dp[i][1]%=mod;//先求dp[i][1]
dp[i][1]=dp[i][1]*fastpow(2,i)%mod*inv((fastpow(2,i)-1+mod)%mod)%mod;
for(int j=2;j<=i;j++)dp[i][j]=(inv2*dp[i-1][j-1]%mod+inv2*dp[i][j-1]%mod)%mod;
}
for(int i=1;i<=n;i++)cout<<dp[n][i]<<" ";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;//cin>>T__;
while(T__--)solve();
return 0;
}