Logo __vector__ 的博客

博客

How to AK ABC243

...
__vector__
2025-12-01 12:55:44

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-03-17 22:04:44

前言:

赛场上 C 题吃了 $23$ 个罚时,D题时间不够了(赛后 1 分钟想出正解)。
结果呢?
rating: $\color{brown} 500 \rightarrow 497 \color{black}(-3)$
rating 暴跌 3 px

A - Shampoo

暴力即可。

AC 代码:

#include <bits\/stdc++.h>
using namespace std;
int main()
{
    int v,a,b,c;
    cin>>v>>a>>b>>c;
    int cnt=2;
    while(1)
    {
        cnt++;
        cnt%=3;
        if(cnt==0)
        {
            if(v<a)
            {
                printf("F");
                return 0;
            }
            v-=a;
        }
        if(cnt==1)
        {
            if(v<b)
            {
                printf("M");
                return 0;
            }
            v-=b;
        }
        if(cnt==2)
        {
            if(v<c)
            {
                printf("T");
                return 0;
            }
            v-=c;
        }
    }
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

B - Hit and Blow

暴力即可。

AC 代码:

#include <bits\/stdc++.h>
using namespace std;
const int maxn=1005;
int a[maxn],b[maxn];
int n;
int main()
{
    cin>>n;
    int ans1=0,ans2=0;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)cin>>b[i];
    for(int i=1;i<=n;i++)if(a[i]==b[i])ans1++;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j)continue;
            if(a[i]==b[j])ans2++;
        }
    }
    printf("%d\n%d",ans1,ans2);
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

C - Collision 2

题目大意:

给你 $n$ 个点的坐标 $(x_i,y_i)$。每个点都有一个运动方向(向左或向右),假如让每个点不断运动,是否会有两个点相遇?
保证初始时任意两个点坐标不同。

题目分析:

维护一个结构体 $node$,存储每个点的坐标和方向。然后根据横坐标对其从小到大进行排序。
定义一个 map 容器:

map<pair<int,char>,vector<int>> im; 

代表纵坐标为 pair.first,方向为 pair.second 的所有点的横坐标有哪些(由 vector 存储)
然后遍历每一个点,假设其纵坐标为 $y$ ,横坐标为 $x$ 如果其方向为向左,那么看一下存储 (( 当前纵坐标上所有向右的点 ) 的横坐标) 的 vector 的第一个元素,如果第一个元素的值小于 $x$,那么输出 Yes,否则则说明当前 vector 没有元素符合小于等于 $x$,那么跳过。(因为已经从小到大排序了)
如果当前方向为右,应该能举一反三吧我就懒得说了

AC 代码:

#include <bits\/stdc++.h>
using namespace std;
map<pair<int,char>,vector<int>> im; 
const int maxn=2e5+5;
struct Node
{
    int x,y,s;
}node[maxn];
int n;
char s[maxn];
inline bool cmp(Node a,Node b)
{
    return a.x<b.x;
}
int main()
{
    int cnt=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&node[i].x,&node[i].y);
    }
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        node[i].s=s[i];
    }
    sort(node+1,node+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        im[make_pair(node[i].y,node[i].s)].emplace_back(node[i].x);
    }
    for(int i=1;i<=n;i++)
    {
        if(node[i].s=='L')
        {
            for(int pos:im[make_pair(node[i].y,'R')])
            {
                if(pos<=node[i].x)
                {
                    printf("Yes");
                    return 0;
                }
                else
                {
                    break;
                }
            }
        }
        if(node[i].s=='R')
        {
            int po=im[make_pair(node[i].y,'L')].size()-1;
            if(po<0)continue;
            if(im[make_pair(node[i].y,'L')][po]>=node[i].x)
            {
                printf("Yes");
                return 0;
            }
        }
    }
    printf("No");
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

D - Moves on Binary Tree

题目分析:

可以发现到达父节点就是 $x$ >>= $1$, 到达左子节点就是 $x$ <<= $1$,到达右子节点就是 $x$ = $x$ << $1$ | $1$。
把二进制弄成高精,string 存一下。最后 bitset 转换成 long long 就行了。

AC 代码:

#include <bits\/stdc++.h>
using namespace std;
const int maxn=1e6+5;
char s[maxn];
typedef long long ll;
ll n,x;
string ans;
int main()
{
    scanf("%lld%lld",&n,&x);
    scanf("%s",s+1);
    bitset<100> bit(x);
    ans+=bit.to_string();
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='U')
        {
            ans.pop_back();
        }
        if(s[i]=='L')
        {
            ans.append("0");
        }
        if(s[i]=='R')
        {
            ans.append("1");
        }
    }
    bitset<maxn> out(ans);
    printf("%lld",out.to_ulong());
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

E - Edge Deletion

题目分析:

floyd 求出每两个点之间最短路,然后枚举每一条边判断是否可以被其他边替代,如果当前边可以被替代那么 $ans$++,最后输出 $ans$

AC 代码:

#include <bits\/stdc++.h>
using namespace std;
const int maxn=305;
const int maxm=45000;
int n,m;
int imap[maxn][maxn];
int a[maxm],b[maxm],c[maxm];
signed main()
{
    memset(imap,0x3f3f3f3f,sizeof(imap));
    scanf("%d%d",&n,&m);
    int ai,bi,ci;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&ai,&bi,&ci);
        a[i]=ai;
        b[i]=bi;
        c[i]=ci;
        imap[ai][bi]=ci;
        imap[bi][ai]=ci;
    }
    for(int mid=1;mid<=n;mid++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                imap[i][j]=min(imap[i][j],imap[i][mid]+imap[mid][j]);
            }
        }
    }
    int ans=0;
    bool flag;
    for(int i=1;i<=m;i++)
    {
        flag=0;
        for(int j=1;j<=n;j++)
        {
            if(imap[a[i]][j]+imap[j][b[i]]<=c[i])
            {
                flag=1;
            }
        }
        ans+=flag;
    }
    printf("%d",ans);
    return 0;
}

F - Lottery

题目前置:

有理数取余,应该谁都会吧(清北灵堂清北学堂好像讲过)

题目分析:

设 $p_i = \frac{W_i}{\Sigma^N_{j=1}W_j}$
显然,第 $i$ 个物品抽到了 $c_i$ 个的概率为 ${p_i}^{c_i}$。所有的物品综合起来,概率即为 $\prod_{i=1}^{n} {p_i}^{c_i}$
此处引用 @sunjiangnan55 的图片:

根据这个定理,假设抽了 $k$ 次。把 $k$ 次抽奖机会分配给 $n$ 个物品,每个物品抽到了 $c_i$ 个,那么排列方式就有 $\frac{k!}{\prod_{i=1}^{n} {c_i!}}$ 个。
两个概率乘起来就是(抽奖抽了 $k$ 次之后,第 $i$ 号物品获得 $c_i$ 个)的概率:$\prod_{i=1}^{n} {p_i}^{c_i} \cdot \frac{k!}{\prod_{i=1}^{n} {c_i!}}$
来通过 dp 求解。
设 $f[i][j][k]$ 表示 $i$ 个物品,抽了 $k$ 次后得到了 $j$ 个不同奖品的方案数。
状态转移: $f[i][j][k] = (f[i-1][j][k] + \Sigma_{l=1}^{k} f[i-1][j-1][k-l] \cdot {p_i}^{l} \cdot {l!}^{998244351}) mod 998244353$
这里我解释一下,${l!}^{998244351}$ 代表 $l!$ 的逆元;$l$ 的意思就是新抽了多少次奖;从 $j-1$ 转移过来是代表增加了一个种类的奖品,因为只增加了一个种类 ,但是抽中了 $l$ 个奖品,所以这些新抽中的一定是同一种类的(这个新种类就是当前第 $i$ 个物品),这种情况发生的概率为 ${p_i}^{l}$,所以要乘上 ${p_i}^{l}$。

AC 代码:

#include <bits\/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=55;
const int mod=998244353;
int n,m,k;
int w[maxn];
ll p[maxn];\/\/每个点被选中的概率
ll sum;\/\/w总和
inline int ksm(ll a,ll b,ll mod)
{
    ll ret=1;
    while(b)
    {
        if(b&1)ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
inline int ny(ll a,ll mod)
{\/\/逆元
    return ksm(a,mod-2,mod);
}
ll fact[maxn];
ll f[maxn][maxn][maxn];
\/\/i个物品,抽了k次之后得到了j个不同奖品
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        sum+=w[i];
    }
    for(int i=1;i<=n;i++)
    {\/\/预处理被选概率
        p[i]=((ll)w[i]*ny(sum,mod))%mod;
    }
    fact[0]=1;
    for(int i=1;i<maxn;i++)
    {
        fact[i]=fact[i-1]*i%mod;
    }
    f[0][0][0]=1;
    for(int i=1;i<=n;i++)
    {
        int tmp=min(i,m);
        for(int j=0;j<=tmp;j++)
        {
            for(int kkksc03=0;kkksc03<=k;kkksc03++)
            {
                f[i][j][kkksc03]=f[i-1][j][kkksc03];
                if(j>0)
                {
                    for(int l=1;l<=kkksc03;l++)
                    {
                        f[i][j][kkksc03]+=(f[i-1][j-1][kkksc03-l]*ksm(p[i],l,mod)%mod*ny(fact[l],mod)%mod);
                        f[i][j][kkksc03]%=mod;
                    }
                }        
            }
        }
    }
    printf("%lld",f[n][m][k]*fact[k]%mod);
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

G - Sqrt

题目分析:

很容易得到一个 $O(\sqrt N)$ 的垃圾做法。显然,$f[x] = \Sigma_{i=1}^{\sqrt N} f[i]$,$f[1]=1$
尝试对此改进,可以通过只枚举 $1 \rightarrow N^{\frac{1}{4}}$,那么显然,对于 $i$,$f[i]$ 的 $i$ 可以由 从$\sqrt N$ 到 $i^2$的所有数一步步按题意操作得来。
所以答案就是 $\Sigma_{i=1}^{n^{\frac{1}{4}}} (f[i] \cdot(\sqrt N - i^2 + 1))$

AC代码:

#include <bits\/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=6e6+5;
int t;
ll x;
ll great_sqrt(ll sum)
{
    ll res=(ll)sqrt(sum-0.1);
    while(res*res<=sum)res++;
    return res-1;
}
ll dp[maxn];
int main()
{
    dp[1]=1;
    for(int i=2;i<=55000;i++)
    {
        int tmp=great_sqrt(i);
        for(int j=1;j<=tmp;j++)
        {
            dp[i]+=dp[j];
        }
    }
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&x);
        if(x<=55000)
        {
            printf("%lld\n",dp[x]);
            continue;
        }
        ll x2=great_sqrt(x);
        ll x3=great_sqrt(x2);
        ll ans=0;
        for(ll i=1;i<=x3;i++)
        {
            ans+=(x2-i*i+1)*dp[i];
        }
        printf("%lld\n",ans);
    }
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。