牛客多校2024-8

K - Haitang and Ava

怎么还有人签到题wa啊/kk
定义以下的字符串是合法的:

  1. 空字符串
  2. \(S\)是合法的,那么\(S\)+avaava+\(S\)都是合法的
  3. \(S\)是合法的,那么\(S\)+avavaavava+\(S\)都是合法的
    给定一个字符串,判断是否合法。

以v为分隔计数a,容易发现计数数组中除了开头和末尾不能有两个1相邻,2可以任意。
(然后发现可以直接从开头截取avava或者ava,前者优先,简单了114514倍)


int T = next();
while(T--)
{
    string str; cin>>str;

    bool no = 0;
    for(auto ch: str) if (ch != 'a' && ch != 'v') no = 1; 
    if (no) { cout<<"No"<<endl; continue; }

    int cnt = 0;
    vector<int> a;
    rep(i, 0, str.size())
    {
        if (str[i] == 'a') cnt++;
        else a.push_back(cnt), cnt = 0;
    }

    if (cnt != 1 || a[0] != 1) no = 1; // 开头或末尾a个数>=2
    if (no) { cout<<"No"<<endl; continue; }

    a.erase(a.begin());

    rep(i, 0, a.size())
    {
        if (i && a[i] == a[i-1] && a[i] == 1) no = 1;
        if (!a[i] || a[i] > 2) no = 1; // 连续的v个数>1或连续的a个数>2
    }

    if (no) cout<<"No"<<endl;
    else cout<<"Yes"<<endl;
}

int T = next();
while(T--)
{
    string str; cin>>str;

    bool no = 0;
    int i = 0;
    while(i < str.size())
    {
        if (str[i] == 'a' && str[i+1] == 'v' && str[i+2] == 'a')
        {
            if (i+4 < str.size() && str[i+3] == 'v' && str[i+4] == 'a') i += 5;
            else i += 3;
        }
        else { no = 1; break; }
    }
    
    if (no) cout<<"No"<<endl;
    else cout<<"Yes"<<endl;
}


A - Haitang and Game

AliceBob 对于集合\(a\)中的数玩一个游戏。Alice 先行。一次行动为:
对于\(x, y \in S\)并且\(gcd(x, y) \notin S\),将\(gcd(x,y)\)插入\(S\)
没法行动的人输掉。理想情况下输出胜者。
\(T \leq 20,n \leq 1e5,a_i \leq 1e5\)


首先发现插入\(gcd(x,y)\)并不能让另一个玩家有更多操作选项,因此不存在博弈。
问题从而转化成了求\(gcd(x,y) \notin S\)的数量,从而得出奇偶。
思考一会儿发现,单从\(a_i\)的质因数讨论可能的\(gcd\)困难重重。观察到\(a_i \leq 1e5\),可以直接从值域入手。考虑一个数\(t \notin a\),记\(a'=\{kt\ |\ k \in \mathbb{N_+},kt \in a\}\),那么\(gcd(a')=pt(p \in \mathbb{N_+})\)。如果\(p=1\),则\(t\)会被加入进\(a\)。枚举\(t\)模拟上述过程即可。


int T = next();
while(T--)
{
    memset(vis, 0, sizeof(vis));

    int n = next();
    rep(i, 0, n) cin>>w[i], vis[w[i]] = 1;

    int cnt = 0;
    hrp(i, 1, 1e5) if (!vis[i])
    {
        vector<int> v;
        for(int j = i; j <= 1e5; j += i) if (vis[j]) v.push_back(j);
        
        if (v.empty()) continue;
        int gcd = v[0];
        rep(i, 1, v.size()) gcd = __gcd(gcd, v[i]);
        if (gcd == i) cnt++;
    }

    if (cnt%2) cout<<"dXqwq"<<endl;
    else cout<<"Haitang"<<endl;
}


E - Haitang and Math

定义\(S(m)\)\(m\)所有数位和。给定\(n\),求满足\(m \leq n\)\(n\mod m=S(m)\)\(m\)个数。
\(n \leq 1e12\)


因为\(n \leq 1e12\),所以\(S(m) \leq 108\)。从而将条件转化为\(n-S(m) \mod m = 0\)
考虑枚举\(S(m)\)的值,可以得到集合\(R=[n-108, n-1]\)。再判断\(R\)中元素的因数是否等于当前\(S(m)\)的值即可。


然而不幸的是,对于\(R\)中每个元素求质因数会TLE。因此需要优化,从而引出了区间筛这个东西(我也不知道这是什么?)。
对于每个质数\(p \leq sqrt(n)\),我们只需要考虑它们在\(R\)中的倍数即可。因此有两种方法:

  1. 如果\(n\%p > 108\),它不可能是\(R_i\)的因数,因此可以直接跳过。
  2. 只枚举\(R\)\(p\)的倍数。

实测两种方法都跑进了200ms,第一种略快。代码采用的是第一种。


const int U = 1e6+500;
bool isPrime[U];
vector<int> prime;
void Eratosthenes(int n)
{
    isPrime[0] = isPrime[1] = 0;
    hrp(i, 2, n) isPrime[i] = 1;
    hrp(i, 2, n) if (isPrime[i])
    {
        prime.push_back(i);
        for(int j = i; j <= n; j += i) isPrime[j] = 0;
    }
}
vector<pair<int, int>> fac[120];
vector<int> fs[120];
int ms[120];
int S(int e)
{
    int ans = 0;
    while(e) ans += e%10, e /= 10;
    return ans;
}
void dfs(int p, int e, int num)
{
    if (e == fac[p].size())
    {
        fs[p].push_back(num);
        return;
    }

    dfs(p, e+1, num);
    hrp(i, 1, fac[p][e].second) dfs(p, e+1, num *= fac[p][e].first);
}

Eratosthenes(1e6+100);

int T = next();
while(T--)
{
    int n = next(), cnt = 0;

    hrp(i, 1, 108) ms[i] = n-i, fs[i].clear(), fac[i].clear();
    for(int i = 0; prime[i]*prime[i] <= n; i++)
    {
        if (prime[i] < 108) hrp(j, 1, 108) 
        {
            if (ms[j] < prime[i]) continue;
            int cnt = 0;
            while(ms[j]%prime[i] == 0) ms[j] /= prime[i], cnt++;
            if (cnt) fac[j].push_back({prime[i], cnt});
        }
        else
        {
            int j = n%prime[i], cnt = 0;
            if (j > 0 && j <= 108) while(ms[j]%prime[i] == 0) ms[j] /= prime[i], cnt++;
            if (cnt) fac[j].push_back({prime[i], cnt});
        }
    }
    hrp(i, 1, 108) if (ms[i] > 1) fac[i].push_back({ms[i], 1});
    hrp(i, 1, 108) if (ms[i] > 0) dfs(i, 0, 1);

    hrp(i, 1, 108) for(auto v: fs[i]) if (v != i && S(v) == i) cnt++;

    cout<<cnt<<endl;
}

J - Haitang and Triangle

给定\(n,m\),构造一个满足以下条件的排列,或输出无解:
恰好有\(m\)个长度为\(3\)的子区间,使得这个子区间内的三个数能构成三角形。


显然:\(1,2,3,4,5,...\)是使\(m\)最大的排列。
这道题的关键在于观察到\(m=0\)的排列如何构成,锻炼观察力从现在做起!
观察到如果\(n\)\(3\)的倍数,记\(d=n/3\),那么\(d,2d,3d,d-1,2d-1,3d-1,d-2,...,d+1,2d+1\)\(m=0\)时的一种排列。考虑扩增这个排列,那么\(3d+1\)可以放在左边,\(3d+2\)可以放在右边,分别对应了\(n=3k+1\)\(n=3k+2\)的情况。
再考虑\(m \neq 0\)的情况,可以将\(a=n-m\)先按上述过程排列好。再将剩下\(m\)个数全都排列在排列右边。这种方法对于\(n=3k\)(\(3d+1 > d+1+2d+1\))和\(n=3k+2\)(\(3d+3>3d+2+2d+1\))的情况都成立,但对于\(n=3k+1\)(\(3d+2 = 2d+1+d+1\))的情况不成立。因此当\(m \neq 0\)的时候可以将\(3d+1\)\(3d+2\)换位后再将剩下\(m\)个数全都排列在排列右边。


int T = next();
while(T--)
{
    int n, m;
    cin>>n>>m;

    if (m >= n-2) { cout<<-1<<endl; continue; }

    int a = n-m, d = a/3;
    deque<int> dq;
    rev(i, d, 1) dq.push_back(i), dq.push_back(i+d), dq.push_back(i+2*d);

    if (a%3 == 1)
    {
        if (!m) dq.push_front(a);
        else
        {
            dq.push_back(a);
            dq.push_front(a+1);
            hrp(i, a+2, n) dq.push_back(i);
        }
    }
    else
    {
        if (a%3 == 2) dq.push_front(a-1), dq.push_back(a);
        hrp(i, a+1, n) dq.push_back(i);
    }

    for(auto v: dq) cout<<v<<' ';
    cout<<endl;
}


D - Haitang and Uma Musume

非常困难的题目,当然题目指题面
进行一个赛马娘训练的模拟。


一个坑:只有summer值等于\(1\)的训练需要计数。


const double eps = 1e-6;
const int base[5][5][6] = {{{10, 0, 5, 0, 0, 2}, {0, 9, 0, 4, 0, 2}, {0, 5, 8, 0, 0, 2}, {4, 0, 4, 8, 0, 2}, {2, 0, 0, 0, 9, 4}},
                           {{11, 0, 5, 0, 0, 2}, {0, 10, 0, 4, 0, 2}, {0, 5, 9, 0, 0, 2}, {4, 0, 4, 9, 0, 2}, {2, 0, 0, 0, 10, 4}},
                           {{12, 0, 5, 0, 0, 2}, {0, 11, 0, 4, 0, 2}, {0, 6, 10, 0, 0, 2}, {4, 0, 4, 10, 0, 2}, {3, 0, 0, 0, 11, 4}},
                           {{13, 0, 5, 0, 0, 2}, {0, 12, 0, 4, 0, 2}, {0, 6, 11, 0, 0, 2}, {4, 0, 4, 11, 0, 2}, {3, 0, 0, 0, 12, 4}},
                           {{14, 0, 5, 0, 0, 2}, {0, 13, 0, 4, 0, 2}, {0, 7, 12, 0, 0, 2}, {5, 0, 5, 12, 0, 2}, {4, 0, 0, 0, 13, 4}}};
int cnt[10];
double coe[10] = {-0.2, -0.1, 0, 0.1, 0.2};
// base[lv][type][X]
struct SupportCard
{
    int friendd, drive, train;
    int initial[20], bonus[20];
    void input(void)
    {
        cin>>friendd>>drive>>train;
        rep(i, 0, 5) cin>>initial[i];
        rep(i, 0, 5) cin>>bonus[i];
    }
} sc[10];
struct UmaMusume
{
    int attribute[20];
    int bonus[20];
    void input(void)
    {
        rep(i, 0, 5) cin>>attribute[i];
        rep(i, 0, 5) cin>>bonus[i];
    }
    void check(void)
    {
        rep(i, 0, 5) attribute[i] = min(attribute[i], 1200LL);
    }
    void init(void)
    {
        attribute[5] = 120;
        rep(i, 0, 5) rep(j, 0, 6) attribute[i] += sc[j].initial[i];
        check();
    }
} uma;
struct Training
{
    int summer, weight, drive, type, lv, num;
    int card[20], friendd[20];
    void input(void)
    {
        cin>>summer>>weight>>drive>>type>>num;
        rep(i, 0, num) cin>>card[i]>>friendd[i], card[i]--;
    }
    void level(void)
    {
        if (summer) lv = 4;
        else lv = min(4LL, cnt[type]/4);
    }
    double stimulate(int ability)
    {
        if (weight && !ability) return 0;

        level();
        double delta[10] = {base[lv][type][ability], 1, 1, 1, 1, 1};
        int sumPresentBonus = 0;
        rep(i, 0, num) sumPresentBonus += sc[card[i]].bonus[ability];
        delta[0] += sumPresentBonus;

        rep(i, 0, num) if (friendd[i]) delta[1] *= (1+0.01*sc[card[i]].friendd);

        int sumPresentTrain = 0;
        rep(i, 0, num) sumPresentTrain += sc[card[i]].train;
        delta[2] += 0.01*sumPresentTrain;

        int sumPresentDrive = 0;
        rep(i, 0, num) sumPresentDrive += sc[card[i]].drive;
        delta[3] += coe[drive]*(1+0.01*sumPresentDrive);

        delta[4] += 0.01*uma.bonus[ability];

        delta[5] += 0.05*num;

        double res = 1;
        rep(i, 0, 6) res *= delta[i];
        return res;
    }
} tr[100];

uma.input();
rep(i, 0, 6) sc[i].input();
int n = next();
rep(i, 0, n) tr[i].input();
uma.init();

rep(i, 0, n)
{
    rep(j, 0, 6)
    {
        int t = tr[i].stimulate(j);
        uma.attribute[j] += t+eps; 
    }
    uma.check();
    if (!tr[i].summer) cnt[tr[i].type]++;

    rep(j, 0, 6) cout<<uma.attribute[j]<<' ';
    cout<<endl;
}


G - Haitang and Rock Paper Scissors

dp套dp,咕咕咕



I - Haitang and Ranking

三维偏序,但单点修改第三维。只会cdq的有福了

posted @ 2024-09-15 20:43  Loxilante  阅读(14)  评论(1编辑  收藏  举报