• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
nannandbk
博客园    首页    新随笔    联系   管理    订阅  订阅
Educational Codeforces Round 156 (Rated for Div. 2) ABCD

Educational Codeforces Round 156 (Rated for Div. 2) ABCD

A. Sum of Three

题意:给定正整数 \(n\),判断是否存在正整数 \(a\),\(b\),\(c\) 满足:

  • \(a+b+c=n\)。
  • \(a\),\(b\),\(c\) 均不是 \(3\) 的倍数。

如存在,输出 YES 并构造一组方案,否则输出 NO 。

思路:

法一:我们分类讨论。

根据余数分为:

  • \(n \% 3=0\)

    最小的合法数是\(12=1+4+7\)

  • \(n \% 3=1\)

    最小的合法数是\(7=1+2+4\)

  • \(n \% 3=2\)

    最小的合法数是\(11=1+2+8\)

那么我们发现,\(n\le 6\)的都是不合法的,其他情况可以拆分成\(1,4,n-5\)或者\(1,2,n-3\)

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        int n; cin>>n;
        if(n<=6)
        {
            cout<<"NO\n";
            continue;
        }
      
        if(n%3==0)//12 = 1 + 4 + 7
        {
            if(n<12){
                cout<<"NO\n";
                continue;
            }
            else
            {
                cout<<"YES\n";
                cout<<1<<" "<<4<<" "<<n-5<<"\n";
            }
        }
        else if(n%3==1)//7 = 1 + 2 + 4
        {
            if(n<7)
            {
                cout<<"NO\n";
                continue;
            }
            cout<<"YES\n";
            cout<<1<<" "<<2<<" "<<n-3<<"\n";
        }
        else if(n%3==2)//11 = 1 + 2 + 8
        {
            if(n<8)
            {
                cout<<"NO\n";
                continue;
            }
            cout<<"YES\n";
            cout<<1<<" "<<2<<" "<<n-3<<"\n";
        }
    }
    return 0;
}

法二:如果不想分类讨论呢,我们可以手玩几个样例,大胆猜测,前两个数一定能在10以内确定下来,暴力枚举即可。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;

void solve(int n)
{
    for(int x = 1; x <= n; x++)
    {
        for(int y = x + 1;y < n-x-y; y++)
        {
            int z = n-x-y;
            if(x % 3 && y % 3 && z % 3 && z > 0)
            {
                cout<<"YES\n";
                cout<<x<<" "<<y<<" "<<z<<"\n";
                return;
            }
        }
    }
    cout<<"NO\n";

    return;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        int n; cin>>n;
        solve(n);
    }


    return 0;
}

B. Fear of the Dark

题意:起点是\((0,0)\),给你终点\((p_x,p_y)\)和两个光源\((A_x,A_y),(B_x,B_y)\)。人从起点到终点且所走的路线一定要被光源覆盖。问最小的光源半径是多少?

思路:考虑合法情况:

  • 两个点被同一个光源覆盖
  • 两个点分别被不用光源覆盖,且两个圆有交(相切也可以)。

我们考虑二分答案,去check就行了

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
double px,py,ax,ay,bx,by;
const double eps = 1e-9; 

double get(double x1,double y1,double x2,double y2)
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

bool in(double r,double d)
{
    return d<=r;
}

bool check(double r)
{
    double da = get(px,py,ax,ay),db = get(px,py,bx,by);
    double do1 = get(0,0,ax,ay),do2 = get(0,0,bx,by);
    if((in(r,da)&&(in(r,do1)))||(in(r,do2)&&in(r,db)))
    {
        return true;
    }
    else
    {
        double ab = get(ax,ay,bx,by);
        if(((in(r,da)&&in(r,do2))||(in(r,db)&&in(r,do1)))&&ab<=2*r)
        {
            return true;
        }
    }
    return false;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        cin>>px>>py>>ax>>ay>>bx>>by;
        double l = 0, r = 1e4, mid, ans;
        while(fabs(l-r)>=eps){
            mid = (l+r)/2;
            if(check(mid))
                r = mid;
            else
                l = mid;
        }
        ans = mid;
        printf("%.12f\n",ans);
    }


    return 0;
}

C. Decreasing String

题意:有 \(t\) 组测试点,每组测试点给出字符串 \(s_1\) 和 一个整数 \(pos\)。

以下列规则得到字符串 $ S $ :

  • 从 $ s_{i - 1} $ 中删除一个字符,字典序最小的记为 $ s_i $
  • $S = s_1 + s_2 + \dots + s_n $

输出字符串 $ S $ 第 \(pos\) 个位置上的字符(即 $ S_{pos} $)。

思路:这题很神奇,思路很巧妙,本人很菜没想出,看jls的代码学习的。

正解是单调栈。

我们每次得到的字符串长度是上次的长度-1。考虑:我们要求最终串的\(pos\)位,我们真的需要知道最终的串长什么样子吗?

不用的,因为\(S = s_1+s_2+...+s_n\),我们只要确定我们的\(pos\)是子串\(s_x\)种的第几位就行了。那么我们得先确定出\(s_x\)是第几个串,和第\(x\)个串的第几个。

我们\(for(i=0~n-1)\)

第\(i\)次的长度是\(n-i\),一旦不够减了,说明\(pos\)在我们的这个串\(i\)里面了

确定出这两个,我们只要求出第\(i\)个串长什么样就可以轻松求得答案了。

因为是第\(i\)个串,说明我们删了\(i\)个字符,我们要保证字典序最小,说明加入的一定是个尽量单调递减的串。我们考虑用单调栈维护。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        string s; cin>>s;
        ll n = s.size();
        ll pos; cin>>pos;
        pos--;
        int x, y;
        for (int i = 0; i < n; i++) {
            int len = n - i;
            if (pos < len) {
                x = i;//编号即删的次数
                y = pos;//位置
                break;
            }
            pos -= len;
        }

        string ans = "";//构造第i个串
        for(auto c : s) {
            while(x > 0 && !ans.empty() && c < ans.back()) {
                ans.pop_back();
                x--;
            }
            ans += c;
        }
        cout<<ans[y];
    }

    return 0;
}

D.Monocarp and the Set

题意:

Monocarp有 \(n\) \((2 \leq n \leq 3 \times 10^5)\)个数字 \(1,2,...n\) 以及一个空集合。他每次以一定顺序将数字加入集合。每个步骤中,他会将一个不在集合中的数加入集合。换言之,最终加入集合的顺序是n的一个排列。

从第二次开始,每次monocarp将元素添加到集合中时,他会写下一个字符

  • 如果Monocarp尝试插入的元素成为集合中的最大元素,Monocarp将写下>

  • 如果Monocarp尝试插入的元素成为集合中的最小元素,Monocarp将写下<

  • 如果不符合以上两种情况,Monocarp将写下?

给你一个长度为 \(n-1\) 的字符串 \(s\) ,代表Monocarp按顺序写下的字符,你需要处理 \(m\) \((1 \leq m \leq 3 \times 10^5)\) 次询问,每个询问格式如下:

  • \(i\) \(c\) - 将 \(s_i\) 替换为字符 \(c\)

对于初始字符串和每次查询,请输出一个整数,代表可以得到字符串 \(s\) 的不同插入顺序的数量。由于答案可能很大,只需给出这个数模 \(998244353\) 的结果。

思路:介个题其实思维难度上不大,但是看着吓人。

我们先预处理出初始答案。我们正向考虑,如果当前是\(?\),那么能插入的位置是除了开头和结尾的任意一个(方案数是元素个数-2)。如果是\(> or <\),那么一定是插入到一头或一尾(方案数是1)。

那么:

  • 是\(?\):\(f[i] = f[i-1]*(i-2)\)
  • 是\(> or <\):\(f[i] = f[i-1]*1\)

对于修改操作:如果更改了,其实就是乘上原方案数的逆元再乘修改后方案数即可。

注意:考虑不合法情况,如果一开始是\(?\)肯定是无解的,特判一下就行了。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p = 998244353;
const int N = 3e5 + 10;
int n,m;
string s;
bool fg;
ll ans = 1,inv[N];
void init()
{
    s = "?"+s;
    fg = (s[1]!='?');
    for(int i = 2;i < n; i++)
        ans = (ans*(s[i]=='?'?i-1:1))%p;
    inv[1] = 1;
    for (int i = 2; i <= n; ++ i) {
        inv[i] = 1ll * (p - p / i + p) % p * inv[p % i] % p;
    }
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>n>>m>>s;
    init();
    cout<<(fg?ans:0)<<"\n";
    for(int i = 1;i <= m; i++)
    {
        int pos; char ty;
        cin>>pos>>ty;
        if(pos==1)
            fg = (ty!='?');
        else{
            ans = (ans*(s[pos]=='?'?inv[pos-1]:1))%p;
            ans = (ans*(ty=='?'?pos-1:1))%p;
        }
        s[pos] = ty;
        cout<<(fg?ans:0)<<"\n";

    }
    return 0;
}
posted on 2023-11-21 15:59  nannandbk  阅读(34)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3