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

发布时间 2023-11-21 16:04:52作者: nannan4128

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