Logo __vector__ 的博客

博客

How to AK ABC247

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

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-04-15 23:31:13

前言:

整了一周的 whk ,总算能写写代码了。

A - Move Right

签到题

B-Unique Nicknames

这道题虽然也是一个 $O(n^2)$ 的暴力能过的签到题,但我非要调 $O(n log n)$ 的方法,结果吃了 19 个罚时...........(不过还好是虚拟赛)

C-1 2 1 3 1 2 1

这道题直接按照题目模拟即可,签到题 (然而我犯智障吃了一个罚时)

D-Cylinder

这道题还是签到题。
可以定义一个结构体,用来存储连续的一段写有相同数字的球,如下:

struct Node
{
   int sum,val;
}node[maxn];

sum 代表当前段的球数,val 代表当前段的球上面的数字。
node 作为一个栈,加入新的球时,如果新加入的那些球与栈顶的数字相同,那么将它们合并,否则将新加入的球作为一个新的栈顶元素加入栈中。
删除球的时候也差不多,就不说了。

E-Max Min

暴力肯定都会,来说一说如何对暴力进行优化,使其复杂度降为 $O(n)$(就是官方题解的做法)。

  1. 数组中有一部分元素大于 $x$,或小于 $y$,这些元素的存在实际上把数组分成了几个有效的区间。因为任何区间都不可能包含这些元素,所以可以先利用这些元素把数组划分成几个独立的区间。显然,这些区间可以单独计算答案,不会互相干扰,不会互相干扰的原因是如果要干扰肯定需要经过那些 (大于 $x$,或小于 $y$) 的元素,但这是不可能的。
  2. 划分完区间之后,就可以对每个区间从左到右枚举 $l$,每次加上 $r$ 的取值范围大小即可。

代码:

#include <bits\/stdc++.h>
using namespace std;
#define int long long
namespace Main
{
    typedef long long ll;
    const int maxn=2e5+5;
    int a[maxn];
    int sta[maxn];
    int top=0;
    int n,x,y;
    ll get_ans()
    {
        int j=1;
        int cnt_x,cnt_y;
        cnt_x=cnt_y=0;
        ll ans=0;
        for(int i=1;i<=top;i++)
        {
            for(;j<=top&&(cnt_x==0||cnt_y==0);j++)
            {
                if(sta[j]==x)cnt_x++;
                if(sta[j]==y)cnt_y++;
            }
            if(cnt_x&&cnt_y)
            {
            	\/\/此时j-1为r的最小取值
                ans+=(ll)(top-j+2);
            }
            if(sta[i]==x)cnt_x--;
            if(sta[i]==y)cnt_y--;\/\/l向右移动,清除当前
        }
        return ans;
    }
    void main()
    {
       scanf("%d%d%d",&n,&x,&y);
       for(int i=1;i<=n;i++)
       {
           scanf("%d",&a[i]);
       }
       ll out=0;
       int i=1;
        while(i<=n)
        {
            top=0;
            for(;i<=n&&(a[i]<=x)&&(a[i]>=y);i++)
            {
                sta[++top]=a[i];
            }
            if(top)
            {
                out+=get_ans();
            }
            else
            {i++;}
        }
        cout<<out;
    }
}
signed main()
{
    Main::main();
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}

F - Cards

我是照着 $\color{black}\text{c}\color{red}\text{zyjr}$ 大佬的代码才看懂题解的。(atcoder id=cz_yjr)

考虑把每对 $(p_i,q_i)$ 相连,这样就形成了一些连通块,例如 $(a \rightarrow b) \rightarrow (b \rightarrow c) \rightarrow (c \rightarrow d)$ 这样的,设第 $i$ 个连通块大小为 $size_i$,那么每个连通块都要选择一些不同的数字,使得所有最后所有连通块选择的数字正好有 $n$ 个不同的。
可以提前预处理出连通块大小为 $i (i \in [1,n])$ 时的方案数,记为 $f_i$。

  1. $f_1 = 1,f_2=3$
  2. $f_i = f_{i-1}+f_{i-2} (i \ge 3)$
    最后答案就是所有连通块方案数相乘

评论

暂无评论

发表评论

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