AtCoder Beginner Contest 295
题解报告
基本的一些理解和问题都在注释中
A:Probably English
//水题
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <unordered_map>
using namespace std;
const int maxn=1e3+10;
string s[maxn];
unordered_map<string,bool> mp;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
mp["and"]=true;
mp["not"]=true;
mp["that"]=true;
mp["the"]=true;
mp["you"]=true;
int N;cin>>N;
bool Has=false;
for(int i=0;i<N;i++)
{
cin>>s[i];
if(mp.count(s[i]))Has=true;
}
if(Has)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
B:Bombs
//水题
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <unordered_map>
using namespace std;
const int maxn=1e3+10;
char w[maxn][maxn];
int dirx[]={0,0,-1,1};
int diry[]={1,-1,0,0};
int N,M;
void Find(int x,int y,int step)
{
if(!step)return;
for(int i=0;i<4;i++)
{
int fx=x+dirx[i];
int fy=y+diry[i];
if(fx>=1&&fy>=1&&fx<=N&&fy<=M)
{
if(w[fx][fy]=='#')w[fx][fy]='.';
Find(fx,fy,step-1);
}
}
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>N>>M;
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
cin>>w[i][j];
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
if(w[i][j]>'0'&&w[i][j]<='9')
{
Find(i,j,w[i][j]-'0');
w[i][j]='.';
}
for(int i=1;i<=N;i++)
{
for(int j=1;j<=M;j++)
cout<<w[i][j];
cout<<endl;
}
return 0;
}
C:Socks
//水题
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <unordered_map>
using namespace std;
const int maxn=5e5+10;
unordered_map<int,int> mp;
unordered_map<int,bool> vis;
int num[maxn];
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int N;cin>>N;
for(int i=0;i<N;i++)
{
cin>>num[i];
mp[num[i]]++;
}
int ans=0;
for(int i=0;i<N;i++)
{
if(!vis[num[i]])
{
ans+=mp[num[i]]/2;
vis[num[i]]=1;
}
}
cout<<ans<<endl;
return 0;
}
//状态压缩和一般判断奇偶个数用的个数的前缀和
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <unordered_map>
using namespace std;
const int maxn=5e5+10;
long long states[maxn];
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
string s;cin>>s;
states[0]=1;//这个很重要别忘记了。
int len=s.size(),state=0;
for(int i=0;i<len;i++)
{
int number=s[i]-'0';
state^=(1<<number);
states[state]++;
}
long long ans=0;
for(int i=0;i<3000;i++)
ans+=(states[i]*(states[i]-1))/2;
cout<<ans<<endl;
return 0;
}
E:Kth Number
注意一个 $\color{#CD2990}{转换} $:
这个转换应该是这道题目最重要的地方
相应的解释:
\({\quad\quad}\) 如果一个数的概率是\(P(X=i)\),那么它在采用这这种方法累加的时候,前面的小于等于 \(\color{#CD2990}{i}\) 的数 \(\color{#CD2990}{j}\) 的\(P\{X\geqslant j\}\)都包含这个数的概率,则有: $$\sum_{j=1}^{i}P(X=i)$$ Tips:\(P\{X\geqslant 1\}至P\{X\geqslant i\}都包含了P(X=i)\)
就刚好可以等于这个数乘以它的概率$$\sum_{j=1}^{i}P(X=i)=i\times P(X=i)$$
这个公式适用于所有这种形式的求期望的方法,以后遇到求期望的时候就可以考虑下需不需要转换
//这题主要看公式,然后通过数学期望求和的公式看出相应的转换。
//有点困难。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=2e3+10;
const int MOD=998244353;
int nums[maxn];
int sum[maxn];//大于等于i的数。
ll C[maxn][maxn];//先预处理组合数
void init()
{
for(int i=0;i<maxn;i++)
{
C[0][i]=1;
for(int j=1;j<=i;j++)
C[j][i]=(C[j-1][i-1]+C[j][i-1])%MOD;
}
}
ll fpow(ll A,ll B,ll MOD)
{
ll ans=1;
A%=MOD;
while(B)
{
if(B&1)ans=(ans*A)%MOD;
B>>=1;
A=(A*A)%MOD;
}
return ans;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
init();
int N,M,K;cin>>N>>M>>K;
for(int i=1;i<=N;i++)
{
int x;cin>>x;
nums[x]++;
}
for(int i=M;i>=1;i--)sum[i]=sum[i+1]+nums[i];//大于等于这个数的有几个。
ll ans=0;
for(int i=1;i<=M;i++)//枚举K可能有的值
{
int need=N-K+1;//这个位置,需要的大于等于的数量。
if(sum[i]>=need)//大于等于需要填充的,就直接加一,百分百的概率。
ans=(ans+1)%MOD;
else if(sum[i]+nums[0]>=need)//可以填充。
{
int BE=need-sum[i];//最少需要的大于等于的数。
for(int j=BE;j<=nums[0];j++)//选出一部分作为大于等于的,一部分小于。
{
//当一个数很大的时候,一定要记得什么时候相乘,乘后一定要取模。
ll A=fpow((M-i+1)*fpow(M,MOD-2,MOD),j,MOD);//大于等于的
ll B=fpow((i-1)*fpow(M,MOD-2,MOD),nums[0]-j,MOD);//小于的概率
ans=(ans+C[j][nums[0]]*((A*B)%MOD))%MOD;//注意这里的取模的括号。
}
}
}
cout<<ans<<endl;
return 0;
}
F:substr = S
这里的一个解题思路就是:
利用字符串在数中的位置,然后利用在不同位置时不同数量的对应位置的数字的大小,
当\(S\)=22
,(位置)\(W\)=3
时有每个数的组成为??..??22?
,那么会有一个特征如下:
\(\textcolor{#CD2990}{22}9\) 中包含了10个\(W\)=3
时的22
;(220也算哦)
\(1\textcolor{#CD2990}{22}0\) 中包含了11个\(W\)=3
时的22
;
\(1\textcolor{#CD2990}{22}1\) 中包含了12个\(W\)=3
时的22
;
\(202\textcolor{#CD2990}{22}2\) 中包含了2023个\(W\)=3
时的22
;
规律:
如果有\(W\)=3
时的 \(i\) 个字符串,那么会有这个数为:\((i-1)/10^{W-len}+S*10^{W-len}+(i-1)\%10^{W-len}\)这里只是形象写一下
判断在对应位置时,最多能取到多少个这个位置的数,然后求和就行了。
注意如果字符串的最高位为0,那么一定会有一个数在前面,就会少掉一些数量
可以参考官方题解:详细题解
//根据一个数在不同位置时的个数的特征来进行查找相应的最大值
//然后加上对应位置的时候的数的数量。
//有一点数位dp的味道。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=20;
ll P10[maxn];
ll GetNum(int bits,ll nums,string S)//字符串的位置,字符串的数量,字符串。
{
int len=S.size();
int right=bits-len;//右边可以有的位置。
nums--;
if(S[0]=='0')nums+=P10[right];//因为第一个不能为0,所以必须补个数,所以一个一是必不可少的
//所以nums的数量这里加上P10[right],代表原来的数量要少掉这么多。
return (nums/P10[right])*P10[bits]+atoll(S.c_str())*P10[right]+(nums%P10[right]);
}
ll find(string S,ll x)//需要的字符串和不能超过的数的大小。
{
int len=S.size();
ll res=0;
for(int i=len;i<17;i++)//1e16最多有17位数,小于1e16就一定小于17位
{
ll l=1,r=P10[16-len];//这是能填补的最大的数量
//因为无脑用1e16的话会超出long long
while(l<=r)//往左边找的,
{
ll mid=l+r>>1;
if(GetNum(i,mid,S)>x)r=mid-1;
else l=mid+1;
}//最后重叠的时候,r一定十满足条件的,r一定会在l的左侧,l的左侧一定满足
res+=r;
}
return res;
}
void init()
{
P10[0]=1;
for(int i=1;i<18;i++)
P10[i]=P10[i-1]*10;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
init();//先把十的各种次方都预处理出来,后面用来划分数要频繁用到。
int T;cin>>T;
while(T--)
{
string S;
ll L,R;
cin>>S>>L>>R;
cout<<find(S,R)-find(S,L-1)<<endl;
}
return 0;
}
G:Minimum Reachable City
这里注意题目给出的条件就好了
//这题就是合并一个个的联通块的过程。
//但是单纯的去搜索很麻烦,所以要把那些已经合成的连通块看成一点。
//题目给出的条件可以令它成为一棵树
//并且每次的加边都会形成一个环,从而使这颗树的点更少,连通度更大
//这个数的结构是子节点的编号一定比父节点大,所以合成的时候一直往上合成就行了。
//而且题目说第一种操作的时候,一定存在v到u的路径,证明find(v)一定是u的父节点。一直认父亲做父亲就行了。
//这样就可以吧这个环给连起来,且都归属于最上面的点,值最小。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=2e5+10;
int pa[maxn];
int fa[maxn];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
int fx=find(x);
int fy=find(y);
fa[fx]=fy;
}
void init(int N)
{
for(int i=0;i<=N;i++)fa[i]=i;
}
int main(void)
{
int N;scanf("%d",&N);
init(N);
for(int i=2;i<=N;i++)scanf("%d",&pa[i]);//每个人的父节点。
int Q;scanf("%d",&Q);
while(Q--)
{
int op;scanf("%d",&op);
if(op==1){
int u,v;scanf("%d %d",&u,&v);
int fx=find(u);
int fy=find(v);
while(fx!=fy)
{
merge(fx,pa[fx]);//这里父节点一定小于等于它,直接合成就行了,不过顺序不能换,
//一定要是小的认大的做父亲。
fx=find(fx);
}
}else{
int x;scanf("%d",&x);
printf("%d\n",find(x));
}
}
return 0;
}
//好像这里用cin,cout的优化没什么用,所以改用scanf和printf更快一些,快四倍左右。
-------------------------------------------
个性签名:啊啊啊,敲代码真的是太难了!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
努力努力,再努力,哈哈哈(っ•̀ω•́)っ✎⁾⁾!