首页 > 其他分享 >2024牛客暑期多校训练营2

2024牛客暑期多校训练营2

时间:2024-07-18 20:40:05浏览次数:13  
标签:std const int 多校 pos 2024 牛客 ans include

Preface

最下班的一集,100min 的时候手速过了六题,本来以为是完美开场,没想到是坐牢的开始

J 题很快推出了一个 \(O(n)\) 计算的组合式子,然后扔给徐神找生成函数做法,中间给出了几个要写快速阶乘算法的假做法后发现这题不太可做

祁神开始转战 D 题后给了个基于纳什均衡的很对的 DP 做法,然后二分的转移写了半天过不了样例,最后换成枚举后正确性是有保证了,但交上去又 TLE 了

过的人比较多的 G 题因为基本没怎么话时间想因此没意识到是个丁真题,最后徐神发现了 \(\sqrt {1000}\) 以内的质数只有 \(11\) 个,但已经于事无补了

最后靠着前期优秀的罚时苟了个尚可的排名,但后 200min 不出题让人感觉梦回去年啊


Floor Tiles

徐神看了眼就秒了,我题意都不知道,纯纯的被带飞

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    int T; std::cin >> T; while(T--) {
        int n, m, k; std::cin >> n >> m >> k;
        int x, y; std::string t; std::cin >> x >> y >> t;
        x--; y--;
        
        if(k < n + m) {
            std::cout << "No\n";
            continue;
        }
        
        std::vector<std::string> ans(n, std::string(m, 'A'));
        
        if((x + y & 1) ^ (t[0] == 'A'))
            for(int i = 0; i < n; ++i)
                for(int j = 0; j < m; ++j)
                    ans[i][j] = char('A' + (i + j & 1));
        else
            for(int i = 0; i < n; ++i)
                for(int j = 0; j < m; ++j)
                    ans[i][j] = char('B' - (i + j & 1));
        auto is_circle = [&](int i, int j) -> bool {
            return ans[i][j] == 'A' && ans[i][j + 1] == 'B' &&
               ans[i + 1][j] == 'B' && ans[i + 1][j + 1] == 'A';
        };
        int cir = 0, type = 0;
        for(int i = 0; i < n - 1; ++i) for(int j = 0; j < m - 1; ++j) if(is_circle(i, j)) {
            cir += 1;
            if(i == x && j == y) type = 1;
        }
        if(n + m + cir < k) {
            std::cout << "No\n";
            continue;
        }
        for(int i = 0; i < n - 1 && n + m + cir > k; ++i) for(int j = 0; j < m - 1 && n + m + cir > k; ++j) {
            if(is_circle(i, j)) {
                if(type) ans[i][j + 1] = 'A';
                else     ans[i][j] = 'B';
                cir -= 1;
            }
        }
        std::cout << "Yes" << char(10);
        for(int i = 0; i < n; ++i) std::cout << ans[i] << char(10);
    }
    return 0;
}

MST

感觉数据水了赛时随便写了个带 \(\log\) 的东西交上去一发过了,后面发现是有严格根号的做法的说

这种询问集合的题很容易想到根号分治,显然当 \(k_i\) 较大时直接 \(O(m\cdot\alpha(m))\) 跑一个克鲁斯卡尔即可

当 \(k_i\) 较小时理论上我们需要严格的 \(O(k_i^2)\) 做法,很容易想到用 Prim,但问题在于我们无法快速找出这些点之间的边

一种解决方案是对边的存储也使用根号分治,将点按照度数分类

度数 \(>\sqrt m\) 的点数较少,可以用临界矩阵存储;否则剩下的度数 \(\le \sqrt m\) 的点对应的边数较少,可以用邻接表存储

当然比赛时我直接用 map 存两点之间的边,然后还是暴力跑克鲁斯卡尔,交上去也过了

#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
#define RI int
#define CI const int&
using namespace std;
typedef pair <int,int> pi;
const int N=100005,LIM=250;
struct edge
{
    int x,y,v;
    inline edge(CI X=0,CI Y=0,CI V=0)
    {
        x=X; y=Y; v=V;
    }
    friend inline bool operator < (const edge& A,const edge& B)
    {
        return A.v<B.v;
    }
}E[N]; int n,m,q,k,p[N],fa[N]; map <pi,int> rst;
inline int getfa(CI x)
{
    return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
namespace Case1
{
    inline void solve(void)
    {
        RI i,j; for (i=1;i<=k;++i) fa[p[i]]=p[i];
        vector <edge> edges;
        for (i=1;i<=k;++i) for (j=i+1;j<=k;++j)
        {
            int x=p[i],y=p[j]; if (x>y) swap(x,y);
            if (rst.count(pi(x,y))) edges.emplace_back(x,y,rst[pi(x,y)]);
        }
        sort(edges.begin(),edges.end());
        int cnt=0; long long ret=0;
        for (auto [x,y,v]:edges)
        {
            if (getfa(x)!=getfa(y))
            {
                fa[getfa(x)]=getfa(y);
                ++cnt; ret+=v;
            }
            if (cnt==k-1) break;
        }
        if (cnt!=k-1) puts("-1"); else printf("%lld\n",ret);
    }
};
namespace Case2
{
    bool key[N];
    inline void solve(void)
    {
        RI i; for (i=1;i<=k;++i) fa[p[i]]=p[i],key[p[i]]=1;
        int cnt=0; long long ret=0;
        for (i=1;i<=m;++i)
        {
            int x=E[i].x,y=E[i].y;
            if (!key[x]||!key[y]) continue;
            if (getfa(x)!=getfa(y))
            {
                fa[getfa(x)]=getfa(y);
                ++cnt; ret+=E[i].v;
            }
            if (cnt==k-1) break;
        }
        for (i=1;i<=k;++i) key[p[i]]=0;
        if (cnt!=k-1) puts("-1"); else printf("%lld\n",ret);
    }
}
int main()
{
    RI i; for (scanf("%d%d%d",&n,&m,&q),i=1;i<=m;++i)
    {
        scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].v);
        if (E[i].x>E[i].y) swap(E[i].x,E[i].y);
    }
    for (sort(E+1,E+m+1),i=1;i<=m;++i)
    if (!rst.count(pi(E[i].x,E[i].y))) rst[pi(E[i].x,E[i].y)]=E[i].v;
    while (q--)
    {
        for (scanf("%d",&k),i=1;i<=k;++i) scanf("%d",&p[i]);
        if (k<=LIM) Case1::solve(); else Case2::solve();
    }
    return 0;
}

Red Walking on Grid

首先不难发现假设起点在一个连通块的最左边,则存在一种最优的方案使得全程不会往左走

那么在第一行的点只有向下和向右两种操作了;在第二行的点就只能向上或向下

考虑 DP,令 \(f_{i,j,0/1}\) 表示当前位于 \((i,j)\) 位置,与其同列的另一个格子是否被经过的最长长度,转移十分显然

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1e6+5;
int n,f[2][N][2]; char a[2][N];
int main()
{
    RI i,j; for (scanf("%d",&n),i=0;i<2;++i) scanf("%s",a[i]+1);
    for (j=1;j<=n;++j)
    {
        for (i=0;i<2;++i) if (a[i][j]=='R') f[i][j][0]=max(f[i][j][0],1);
        for (i=0;i<2;++i) if (a[i][j]=='R')
        {
            if (a[i^1][j]=='R') f[i^1][j][1]=max(f[i^1][j][1],f[i][j][0]+1);
        }
        for (i=0;i<2;++i) if (a[i][j]=='R')
        {
            if (j+1<=n&&a[i][j+1]=='R') f[i][j+1][0]=max(f[i][j+1][0],max(f[i][j][0],f[i][j][1])+1);
        }
    }
    int ans=0; for (i=0;i<2;++i) for (j=1;j<=n;++j) ans=max(ans,max(f[i][j][0],f[i][j][1])-1);
    return printf("%d",ans),0;
}

Taking Candies

标算感觉十分奇怪,这里讲下祁神赛时写的做法,正确性是有保证的,但赛时 \(O(n\times x^2)\) TLE 了

首先很容易发现两人每次拿糖果都是拿当前最多的一袋,而关注最后一轮博弈过程,显然是谁钱多谁就拿走最后一袋

考虑从后往前递推,不难发现这是个纳什博弈模型,令 \(f_{i,x}\) 表示还剩下 \(i\) 袋糖果,先手此时手中有 \(x\) 块钱,在剩下的过程中最多能得到的糖果数

考虑枚举先手这一轮使用的筹码数 \(M\),若后手选择阻止先手(需要至少 \(M+1\) 的钱)会导致后续给先手带来的收益更大,则后手显然不会操作,则有转移:

\[f_{i,x}\leftarrow f_{i-1,x-M}+a_i\ (f_{i-1,x-M}+a_i<f_{i-1,x+M+1}) \]

否则分情况讨论一些特殊情况即可,总复杂度 \(O(n\times x^2)\),赛时最后挣扎了一波搞了个记搜剪了点状态,发现还是过不了

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int N = 1e5+5;
int n, xx, yy, tot, A[N];
int f[N][205];

signed main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> xx >> yy;
    tot = xx+yy;
    for (int i=1; i<=n; ++i) cin >> A[i];
    sort(A+1, A+n+1);
    
    for (int x=(tot+1)/2; x<=tot; ++x) f[1][x]=A[1];
    for (int i=2; i<=n; ++i){
        for (int x=0; x<=tot; ++x){
            int L=0, R=min(x, tot-x);  
            for (int M=L; M<=R; ++M){
                if (f[i-1][x-M]+A[i] < f[i-1][x+M+1]){
                    f[i][x] = max(f[i][x], f[i-1][x-M]+A[i]);
                }else{
                    if (M >= tot-x) f[i][x] = max(f[i][x], f[i-1][x-M]+A[i]);
                    else f[i][x] = max(f[i][x], f[i-1][x+M+1]);
                }
            }
        }
    }
    cout << f[n][xx] << '\n';
    return 0;
}

GCD VS XOR

徐神开场写的,还抢到了二血,而我题意都不知道

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    int T; std::cin >> T; while(T--) {
        int64_t x; std::cin >> x;
        int64_t l = x&-x;
        if(l == x) std::cout << "-1\n";
        else       std::cout << (x ^ l) << char(10);
    }
    return 0;
}

Mixed Juice

这场的防 AK,直接弃疗


The Set of Squares

唐完了,早知道就不该把 J 题扔给徐神,不然徐神早一小时看这题可能都出了

需要发现 \(<\sqrt {1000}=32\) 的质数只有 \(11\) 个,这也就意味着每个数最多含有一个 \(\ge 32\) 的质数

因此这些较大的质数只能相互匹配,所以可以分开来处理

而较小的质数由于数量很小,并且我们只关心每个质数此时的次数是奇数还是偶数,因此可以状压

用一个类似背包的 DP 即可做到 \(O(2^{11}\times n)\),转移十分显然

#include<cstdio>
#include<iostream>
#include<vector>
#include<utility>
#define RI register int
#define CI const int&
using namespace std;
typedef pair <int,int> pi;
const int N=1<<11,pri[11]={2,3,5,7,11,13,17,19,23,29,31},mod=1e9+7;
int n,x,f[2][N][2]; vector <pi> vec[N];
inline void inc(int& x,CI y)
{
    if ((x+=y)>=mod) x-=mod;
}
int main()
{
    RI i,j; for (scanf("%d",&n),i=1;i<=n;++i)
    {
        scanf("%d",&x); int mask=0,val=1;
        for (j=0;j<11;++j)
        {
            while (x%pri[j]==0)
            {
                if ((mask>>j)&1) val=1LL*val*pri[j]%mod;
                x/=pri[j]; mask^=(1<<j);
            }
        }
        vec[x].push_back(pi(mask,val));
    }
    int now=0; f[0][0][0]=1;
    auto calc=[&](CI x,CI y)
    {
        int val=1; for (RI i=0;i<11;++i)
        if (((x&y)>>i)&1) val=1LL*val*pri[i]%mod;
        return val;
    };
    for (auto [mask,val]:vec[1])
    {
        int nxt=now^1;
        for (i=0;i<N;++i) f[nxt][i][0]=f[now][i][0];
        for (i=0;i<N;++i) inc(f[nxt][i^mask][0],1LL*f[now][i][0]*val%mod*calc(mask,i)%mod);
        now=nxt;
    }
    for (j=32;j<=1000;++j) if (!vec[j].empty())
    {
        for (i=0;i<N;++i) f[now][i][1]=0;
        for (auto [mask,val]:vec[j])
        {
            int nxt=now^1;
            for (i=0;i<N;++i) f[nxt][i][0]=f[now][i][0],f[nxt][i][1]=f[now][i][1];
            for (i=0;i<N;++i)
            {
                inc(f[nxt][i^mask][0],1LL*f[now][i][1]*val%mod*calc(mask,i)%mod*j%mod);
                inc(f[nxt][i^mask][1],1LL*f[now][i][0]*val%mod*calc(mask,i)%mod);
            }
            now=nxt;
        }
    }
    return printf("%d",(f[now][0][0]-1+mod)%mod),0;
}

Instructions Substring

不难发现枚举左端点 \(l\),假设其对应的第一个走到 \((x,y)\) 的位置为 \(r\),则贡献为 \(n-r+1\)

判断是否走到的充要条件是 \(X_r-X_{l-1}=x\and Y_r-Y_{l-1}=y\),其中 \(X,Y\) 分别为两维坐标的前缀和数组

倒着用 map 维护一下最靠左的 \((X_i,Y_i)\) 即可

#include<cstdio>
#include<iostream>
#include<map>
#include<utility>
#define RI register int
#define CI const int&
using namespace std;
typedef pair <int,int> pi;
const int N=200005;
int n,x,y,px[N],py[N]; map <pi,int> fst; char a[N];
int main()
{
    RI i; scanf("%d%d%d%s",&n,&x,&y,a+1);
    if (x==0&&y==0) return printf("%lld",1LL*n*(n+1)/2LL),0;
    for (i=1;i<=n;++i)
    {
        px[i]=px[i-1]; py[i]=py[i-1];
        if (a[i]=='A') --px[i]; else
        if (a[i]=='D') ++px[i]; else
        if (a[i]=='W') ++py[i]; else
        if (a[i]=='S') --py[i];
    }
    //for (i=1;i<=n;++i) printf("%d %d\n",px[i],py[i]);
    long long ans=0; for (i=n;i>=1;--i)
    {
        fst[pi(px[i],py[i])]=i;
        pi tmp=pi(x+px[i-1],y+py[i-1]);
        if (fst.count(tmp)) ans+=n-fst[tmp]+1;
    }
    return printf("%lld",ans),0;
}

Red Playing Cards

看完题很容易想到一个暴力的 \(O(n^3)\) 区间 DP 做法,但题面显然要求的是 \(O(n^2)\)

考虑对于一个区间,在删除它之前可以先对其内部的一些较小的区间进行操作;而不在其内部的区间要么和它没有影响,要么会导致该区间无法操作

因此把所有区间按照长度从小到大处理,每次枚举其内部从哪些区间进行了操作,总复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;

const int N = 6005;
int n;
int A[N], pos[N][2], len[N], id[N];
int f[N][N];

signed main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    pos[0][0]=0, pos[0][1]=2*n+1, len[0]=2*n+1;
    for (int i=1; i<=2*n; ++i){
        cin >> A[i];
        if (pos[A[i]][0]==0) pos[A[i]][0]=i;
        else pos[A[i]][1]=i, len[A[i]]=pos[A[i]][1]-pos[A[i]][0];
    }
    for (int i=0; i<=n; ++i) id[i]=i;
    sort(id, id+n+1, [&](int a, int b){return len[a]<len[b];});
    for (int dd=0; dd<=n; ++dd){
        int x=id[dd];
        int L=pos[x][0], R=pos[x][1];
        
        f[x][L]=x;
        for (int i=L+1; i<=R; ++i){
            f[x][i] = f[x][i-1]+x;
            if (pos[A[i]][1]==i && pos[A[i]][0]>=L){
                f[x][i] = max(f[x][i], f[x][pos[A[i]][0]-1] + f[A[i]][pos[A[i]][1]]);
            }
        }
        
//         printf("dd=%d f[%d]=%d\n", dd, x, f[x][R]);
    }
//     for (int i=0; i<=n*2+1; ++i) printf("%d ", f[0][i]); puts("");
    cout << f[0][2*n+1] << '\n';
    
    return 0;
}

Involutions

只会这题的组合意义式子,但 \(O(n)\) 的做法还是过不了,生成函数也没办法处理

考虑枚举不动点的个数 \(i\),则剩下的 \(2y=n-i\) 个点构成的置换环长度都必须恰好为 \(2\)

\(C_{2y}^y\) 表示选出 \(y\) 个点作为起点,\(y!\) 表示剩下的 \(y\) 个点关于这些点的匹配方案,但由于两边是对称的,因此要除以 \(2^y\)

最后的式子就是:

\[\sum_{y=0}^{2y\le n} \frac{a^{n-2y}\times C_{2y}^y\times y!}{2^y} \]

然而因为没推递推的关系式,所以根本想不到正解是整式递推(知道了也没用反正写不来),直接弃疗


Postscript

白天打牛客多校,晚上还有 CF 1+2,明天还有杭电多校,真是冲冲又实实啊

标签:std,const,int,多校,pos,2024,牛客,ans,include
From: https://www.cnblogs.com/cjjsb/p/18310413

相关文章

  • GESP编程能力等级认证C++编程真题解析 | 2024年3月五级
    学习C++从娃娃抓起!记录下CCF-GESP备考学习过程中的题目,记录每一个瞬间。附上汇总贴:GESP编程能力等级认证C++编程真题解析|汇总单选题第1题唯一分解定理描述的内容是()?A.任意整数都可以分解为素数的乘积B.每个合数都可以唯一分解为一系列素数的乘积C.两个不同的......
  • 2024夏令营提高1模考0718模拟赛(提高1)补题报告
    2024夏令营提高1模考0718模拟赛(提高1)补题报告$$0718模拟赛(提高1)\\补题报告\2024年7月18日\by\\\唐一潇$$一、做题情况第一题比赛$100/100$,赛后通过第二题比赛$0/100$,赛后通过第三题比赛$0/100$,赛后通过第四题比赛$0/100$,赛后通过比......
  • 2024牛客暑期多校训练营2
    E题意:给定一个数\(x\),找出严格小于\(x\)的一个数\(y\)使得\(gcd(x,y)=x\oplusy\)。赛时小\(wa\)一次,答案就是\(x-lowbit(x)\)(不为\(0\)的前提下)。C题意:HB(补题)题意:给定图,\(q\)次询问,每次给出一个点集,求解该点集的最小生成树。(保证询问的点数之和不超过\(......
  • 【2024】springboot O2O生鲜食品订购
     博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大......
  • 【2024】springboot O2O生鲜食品订购
     博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大......
  • 七款热门企业数据加密软件推荐|2024年加密软件最新整理出炉!
    古言到:“知己知彼,百战不殆。”当今时代,数据为王!企业数据的保护已成为竞争中的关键一环。数据加密软件作为守护企业数字资产的利剑,其重要性日益凸显。2024年,市场上涌现出一批功能强大、特色鲜明的企业数据加密软件。本文精选七款热门软件,为您详细剖析其独特之处,助您在数据安......
  • 七款主流公司办公加密软件推荐|2024年加密软件最新榜单,不得不看
    小李眉头紧锁地说:“最近项目文件泄露事件频发,真是让人头疼。我们得赶紧找一款靠谱的办公文件加密软件,保护公司的核心数据。”小张点头赞同:“是啊,我也一直在关注这个问题。我听说今年市场上出了不少新的加密软件,我们得好好挑一挑。”于是,两人决定一起研究并推荐七款主流的公......
  • 常用的7款加密软件排行榜|办公文件加密(2024干货收藏)
    李明:“王丽,你知道吗?最近我们部门的一些项目资料差点被泄露出去,真是让人担心。”王丽:“是啊,数据安全问题真的不容忽视。我听说现在有很多加密软件可以帮到我们,你有了解过吗?”李明:“确实,我研究了一下,发现有几款加密软件特别适合我们办公使用。不如我们一起来整理一个排行榜,分......
  • 2024-07-18 code标签渲染时会多出空格是怎么回事?
    问题就是我给code标签传递一段源代码,该代码明明没有空格,但实际渲染却多了一串空格?如下图所示: 原因:code标签包裹的内容会原原本本的渲染出来,包括空格。我查看了我的代码: 我是这么写的:<code>{{sourceCode}}</code>{{sourceCode}}前面有空格,code标签直接把它们......
  • 【学术会议征稿】第六届光电材料与器件国际学术会议(ICOMD 2024)
    第六届光电材料与器件国际学术会议(ICOMD2024)20246th InternationalConferenceonOptoelectronicMaterialsandDevices第六届光电材料与器件国际学术会议(ICOMD2024)将于2024年11月1-3日在中国·重庆召开。大会面向基础与前沿、学科与产业,建立起前沿的学术交流平台,将......