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;
}

D:Three Days Ago

//状态压缩和一般判断奇偶个数用的个数的前缀和
#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}{转换} $:

\[(expected{\ \ }value{\ \ }of{\ \ }X)=\sum_{i=1}^{M}(i\times(probability{\ \ }that{\ \ }X=i))=\sum_{i=1}^{M}(probability{\ \ }that{\ \ }X\geqslant i). \]

这个转换应该是这道题目最重要的地方
相应的解释:
\({\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更快一些,快四倍左右。
posted @ 2023-03-31 21:11  WUTONGHUA02  阅读(24)  评论(0编辑  收藏  举报