2024牛客暑期多校训练营8

hl666 / 2024-08-09 / 原文

Preface

最赤石的一集

开局磕磕绊绊一发过了 K,A,E,然后跟榜开 J 出了 114514 个假做法

中间注意到 D 是个模拟+阅读理解的赤石题,滚上去摸了半天被题意杀了一发后过了

(很幸运的是我直接用 double 反而误打误撞过了,没有被卡心态)

然后试图开 G 发现假了,没有想象中的简单,遂全队一起 Rush J

最后搞出来一个差不多对的构造思路,可惜一些边界没处理好最后赛时没改出来,被狠狠拷打了

由于晚上在忙着搞下周百度之星出行的相关事务,G 题的补题就先坑着过两天再说


Haitang and Game

手玩发现这题的博弈部分是假的,只要统计出可以被生成出的数的数量即可,不论使用什么策略最后都是一个萝卜一个坑

从大到小考虑每个数 \(x\) 能否被生成,此时所有 \(>x\) 的数的生成情况已经确定了

要判断是否存在一对数的 \(\gcd\) 等于 \(x\),算是个经典问题了,用约数容斥一下即可解决

#include<cstdio>
#include<iostream>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,a[N],c[N]; long long f[N];
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d",&n); memset(c,0,sizeof(c)); int ret=0;
        for (RI i=1;i<=n;++i) scanf("%d",&a[i]),c[a[i]]=1;
        for (RI i=100000;i>=1;--i)
        {
            f[i]=0;
            for (RI j=i;j<=100000;j+=i) f[i]+=c[j];
            f[i]=f[i]*(f[i]-1)/2LL;
            for (RI j=i*2;j<=100000;j+=i) f[i]-=f[j];
            if (!c[i]&&f[i]>0)
            {
                //printf("gen = %d\n",i);
                c[i]=1; ++ret; f[i]=0;
                for (RI j=i;j<=100000;j+=i) f[i]+=c[j];
                f[i]=f[i]*(f[i]-1)/2LL;
                for (RI j=i*2;j<=100000;j+=i) f[i]-=f[j];
            }
        }
        puts(ret%2?"dXqwq":"Haitang");
    }
    return 0;
}

Haitang and Uma Musume

赤石模拟题,但比起模拟感觉难点在于阅读题意,读懂了之后写起来还是很容易的

值得一提的是我赛时写的时候没管精度直接 double 乱艹搞过去了,没有加 EPS 之类的

同校很多队伍都是用 long double 反而被卡了,只能说是侥幸

#include<cstdio>
#include<iostream>
#include<vector>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
vector <int> base[6][5];
inline void init_base(void)
{
    
    base[1][0]={10,0,5,0,0,2};
    base[1][1]={0,9,0,4,0,2};
    base[1][2]={0,5,8,0,0,2};
    base[1][3]={4,0,4,8,0,2};
    base[1][4]={2,0,0,0,9,4};
    base[2][0]={11,0,5,0,0,2};
    base[2][1]={0,10,0,4,0,2};
    base[2][2]={0,5,9,0,0,2};
    base[2][3]={4,0,4,9,0,2};
    base[2][4]={2,0,0,0,10,4};
    base[3][0]={12,0,5,0,0,2};
    base[3][1]={0,11,0,4,0,2};
    base[3][2]={0,6,10,0,0,2};
    base[3][3]={4,0,4,10,0,2};
    base[3][4]={3,0,0,0,11,4};
    base[4][0]={13,0,5,0,0,2};
    base[4][1]={0,12,0,4,0,2};
    base[4][2]={0,6,11,0,0,2};
    base[4][3]={4,0,4,11,0,2};
    base[4][4]={3,0,0,0,12,4};
    base[5][0]={14,0,5,0,0,2};
    base[5][1]={0,13,0,4,0,2};
    base[5][2]={0,7,12,0,0,2};
    base[5][3]={5,0,5,12,0,2};
    base[5][4]={4,0,0,0,13,4};
}
const double coef[5]={-0.2,-0.1,0,0.1,0.2};
int n,abt[6],abt_mul[6],card[7][10];
int main()
{
    init_base();
    for (RI i=0;i<=4;++i) scanf("%d",&abt[i]); abt[5]=120;
    for (RI i=0;i<=4;++i) scanf("%d",&abt_mul[i]);
    for (RI i=1;i<=6;++i)
    {
        for (RI j=6;j<=8;++j) scanf("%d",&card[i][j]);
        // 0~5:ability; 6:friend; 7:drive_pls; 8:train;
        int dlt; for (RI j=0;j<=4;++j)
        scanf("%d",&dlt),abt[j]=min(1200,abt[j]+dlt);
        for (RI j=0;j<=4;++j) scanf("%d",&card[i][j]);
    }
    scanf("%d",&n); int lv_sp[5]={1,1,1,1,1},cnt_sp[5]={0,0,0,0,0};
    for (RI i=1;i<=n;++i)
    {
        int summer,wgt,drv,tp,m;
        scanf("%d%d%d%d%d",&summer,&wgt,&drv,&tp,&m);
        vector <pair <int,int>> S;
        for (RI j=1;j<=m;++j)
        {
            int x,y; scanf("%d%d",&x,&y);
            S.push_back({x,y});
        }
        for (RI j=0;j<=5;++j)
        {
            int lv=summer?5:lv_sp[tp];
            double dlt=base[lv][tp][j];
            for (auto [x,y]:S) dlt+=card[x][j];
            for (auto [x,y]:S)
            if (y) dlt*=(1.0+0.01*card[x][6]);
            int sum_train=0;
            for (auto [x,y]:S) sum_train+=card[x][8];
            dlt*=(1.0+0.01*sum_train);
            int sum_drv_pls=0;
            for (auto [x,y]:S) sum_drv_pls+=card[x][7];
            dlt*=(1.0+coef[drv]*(1.0+0.01*sum_drv_pls));
            dlt*=(1.0+0.01*abt_mul[j]);
            dlt*=(1.0+0.05*m);
            int Dlt=(int)floor(dlt);
            if (wgt==1&&j==0) Dlt=0;
            if (j<=4) abt[j]=min(1200,abt[j]+Dlt);
            else abt[j]+=Dlt;
        }
        if (!summer)
        {
            ++cnt_sp[tp];
            if (cnt_sp[tp]==4) lv_sp[tp]=min(5,lv_sp[tp]+1),cnt_sp[tp]=0;
        }
        for (RI j=0;j<=5;++j) printf("%d%c",abt[j]," \n"[j==5]);
    }
    return 0;
}

Haitang and Math

不会区间筛,但我们有速率优秀的 Pollard_Rho 爆艹

首先不难想到暴力枚举 \(S(m)=r\),由于取值只有 \([1,108]\) 因此复杂度可以接受

现在式子转为 \((n-r)\bmod m=0\),一个必要条件:\(m\)\(n-r\) 的约数

由于一个数的约数个数大约是 \(O(n^\frac{1}{3})\) 级别的,但我们不能直接 \(O(\sqrt n)\) 地枚举约数,而是得先分解质因数后再组合起来

使用 Pollard_Rho,可以勉强卡着时限通过

#include<cstdio>
#include<iostream>
#include<vector>
#include<random>
#include<algorithm>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
namespace Pollard_Rho
{
	mt19937_64 rnd(20030909);
	inline int quick_mul(CI x,CI y,CI mod)
	{
		return (__int128)(x)*y%mod;
	}
	inline int sum(CI x,CI y,CI mod)
	{
		return x+y>=mod?x+y-mod:x+y;
	}
	inline int sub(CI x,CI y,CI mod)
	{
		return x-y<0?x-y+mod:x-y;
	}
	inline int quick_pow(int x,int p,int mod,int mul=1)
	{
		for (;p;p>>=1,x=quick_mul(x,x,mod)) if (p&1) mul=quick_mul(mul,x,mod); return mul;
	}
	inline bool Miller_Rabin(CI n)
	{
		if (n<=1) return 0;
        vector <int> base={2,3,5,7,11,13,17,19,23};
		for (int x:base)
		{
			if (n==x) return 1;
			if (n%x==0) return 0;
		}
		int m=(n-1)>>__builtin_ctzll(n-1);
		for (int x:base)
		{
			int t=m,a=quick_pow(x,m,n);
			while (t!=n-1&&a!=1&&a!=n-1) a=quick_mul(a,a,n),t*=2;
			if (a!=n-1&&t%2==0) return 0;
		}
		return 1;
	}
	inline int get_fact(CI n)
	{
		if (n%2==0) return 2;
		auto f=[&](CI x)
		{
			return sum(quick_mul(x,x,n),1,n);
		};
		int x=0,y=0,tot=0,p=1,q,g;
		for (int i=0;(i&0xff)||(g=__gcd(p,n))==1;++i,x=f(x),y=f(f(y)))
		{
			if (x==y) x=tot++,y=f(x);
			q=quick_mul(p,sub(x,y,n),n); if (q) p=q;
		}
		return g;
	}
	inline vector <int> solve(CI n)
	{
		if (n==1) return {};
		if (Miller_Rabin(n)) return {n};
		int d=get_fact(n); auto v1=solve(d),v2=solve(n/d);
		auto i1=v1.begin(),i2=v2.begin(); vector <int> ans;
		while (i1!=v1.end()||i2!=v2.end())
		{
			if (i1==v1.end()) ans.push_back(*i2++);
			else if (i2==v2.end()) ans.push_back(*i1++);
			else ans.push_back(*i1<*i2?*i1++:*i2++);
		}
		return ans;
	}
};
using namespace Pollard_Rho;
int t,n,r,ans; vector <int> p,c;
inline int S(int x)
{
    int ret=0; while (x) ret+=x%10,x/=10; return ret;
}
inline void DFS(CI now=0,CI m=1)
{
    if (now>=(int)p.size())
    {
        int tmp=S(m);
        if (tmp==r&&n%m==tmp) ++ans;
        return;
    }
    int cur=1; for (RI i=0;i<=c[now];++i) DFS(now+1,m*cur),cur*=p[now];
}
signed main()
{
    for (scanf("%lld",&t);t;--t)
    {
        scanf("%lld",&n); ans=0;
        for (r=1;r<=min(108LL,n-1);++r)
        {
            auto fact=solve(n-r);
            p.clear(); c.clear();
            for (auto x:fact)
            if (p.empty()||x!=p.back()) p.push_back(x),c.push_back(1); else ++c.back();
            DFS();
        }
        printf("%lld\n",ans);
    }
    return 0;
}

Haitang and Rock Paper Scissors

比赛时候上去冲了一发 DP 发现没法区分最优策略和中间转移的策略,因此感觉是个 DP 套 DP

赛后看题解也没咋看懂,等过段时间看下视频讲解再琢磨下


Haitang and Triangle

最唐氏的一集,全队被这个过穿了的题腐乳,直到最后徐神发现了三个数一组的构造方式,但我上去 Rush 的时候边界写唐了就寄了

大致思路和题解中讲的大差不差,主要要想到构造一种 \(m=0\) 的方案,其它的情况都在上面扩展

以下以 \(n=9,10,11\) 三种情况展示构造思路:

n = 9: 3 6 9 2 5 8 1 4 7 
n = 10: 10 3 6 9 2 5 8 1 4 7 
n = 11: 10 3 6 9 2 5 8 1 4 7 11 
#include<bits/stdc++.h>
using namespace std;

vector<int> pushNone(int x){
    vector<int> res;
    if (x%3>0) res.push_back(x/3*3 + 1);
    int len=x/3;
    for (int i=len; i>0; --i){
        res.push_back(i);
        res.push_back(i+len);
        res.push_back(i+2*len);
    
    }
    if (x%3==2) res.push_back(x);
    return res;
}

void solve(){
    int n, m; cin >> n >> m;
    if (m>=n-2){
        cout << "-1\n";
        return;
    }

    vector<int> ans = pushNone(n-m);
    for (int i=n-m+1; i<=n; ++i) ans.push_back(i);
    if ((n-m)%3==1 && m>0) swap(ans[0], ans[n-m]);
    for (int x : ans) cout << x << ' '; cout << '\n';
}

signed main(){
    ios::sync_with_stdio(0); cin.tie(0);
    int t; cin >> t; while (t--) solve();
    return 0;
}

Haitang and Ava

神秘签到题,我题意都不知道

#include <bits/stdc++.h>

void work() {
    std::string s; std::cin >> s;
    std::vector<bool> dp(s.size() + 1, false);
    dp[0] = true;
    for(int i = 0; i < s.size(); ++i) if(dp[i]) {
        if(i + 3 <= s.size() && strncmp(s.c_str() + i, "ava"  , 3) == 0) dp[i + 3] = true;
        if(i + 5 <= s.size() && strncmp(s.c_str() + i, "avava", 5) == 0) dp[i + 5] = true;
    }
    std::cout << (dp[s.size()] ? "Yes\n" : "No\n");
}

int main() {
    std::ios::sync_with_stdio(false);
    int T; std::cin >> T; while(T--) work();
    return 0;
}

Postscript

看似好转的病情看来又加重了,明天开火车估计要被 Div.2 创飞了