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

2024牛客暑期多校训练营5

时间:2024-07-30 20:20:17浏览次数:5  
标签:const int 多校 times 2024 牛客 include RI define

Preface

坐牢,爽!

前期经典屡次被签到腐乳导致罚时爆炸,写完四题后发现排名已经冲刺 200+ 了,再一看后面的题都过的很少

跟着榜看了一些题后感觉都不太可做,祁神和徐神一直在讨论 J 但我一点不想写大分类讨论 Counting 遂开摆

摆到大概三点半的时候发现 G 题过的队越来越多了,看了眼题意后感觉就是个推结论用 DS 维护的东西

把徐神摇过来后徐神玩了会就给了我个很简单的结论,我翻了个 LCT 板子魔改了下交上去就过了,算是将小局逆转了

赛后补了 K,J 题经典扔给队友写自己白兰去了,下面就只写过了的题的做法,其它题之后再说吧


大力分讨题,首先判掉格子总数为奇数的无解情况;以及 \(1\times 2\) 这种一定有解的情况

手玩一下会发现此时两个限制都加上就一定无解,同时第二个限制其实很强,只有 \(1\times 2k\) 的情况有解

考虑仅有第一个限制时,可以构造出以下两种最小结构单元(数字相同的表示同一块骨牌):

1134
2234
5578
6678
1156
2356
2344

因此我们就有了 \(2\times 2\) 的构造单元和 \(2\times 3\) 的构造单元,简单讨论后发现用这两种单元可以构造除了 \(1\times 2k\) 外的任何情况

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t,n,m,a,b;
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d%d%d%d",&n,&m,&a,&b);
        if (n%2==1&&m%2==1) { puts("No"); continue; }
        if (n>m) swap(n,m);
        if (n==1&&m==2) { puts("Yes"); continue; }
        if (a==1&&b==1) { puts("Yes"); continue; }
        if (a==0&&b==0) { puts("No"); continue; }
        if (a==0)
        {
            if (n==1) puts("No"); else puts("Yes");
        } else
        {
            if (n==1) puts("Yes"); else puts("No");
        }
    }
    return 0;
}

签到题,祁神开场写的,我题目都没看

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

const int N = 1e5+5;
int n, A[N], B[N];

void solve(){
    cin >> n;
    for (int i=1; i<=n; ++i) cin >> A[i];
    for (int i=1; i<=n; ++i) cin >> B[i];
    int cnt1=0, cnt2=0;
    for (int i=1; i<=n; ++i){
        if (A[i]>B[i]) ++cnt1;
        else if (A[i]==B[i]) ++cnt2;
    }
    cout << cnt1 + (cnt2+1)/2 << '\n';
}

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

徐神的观察力,轻松找出结论秒了此题

考虑建一棵新树,对于每条实链,将其缩为一个点,而链底的点可以代表整个点的操作;剩余的虚边连通新树的点

然后问题转化为要在新树上给每个点附上一个排列的值,使得每个点都比其父亲的权值小

通过树形 DP 和归纳法,可以发现答案为 \(\frac{n!}{\prod size_i}\),其中 \(size_i\) 表示节点 \(i\) 在原树上的子树大小,\(i\) 为每条实链链顶的点

在维护答案时我们可以在所有虚边的底端点处统计贡献,用不换根的 LCT 可以轻松维护,复杂度 \(O(n\log n)\)

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,mod=998244353;
int n,q,x,sz[N],tag[N],fact[N],ifac[N],inv[N],ans; vector <int> v[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
	for (ifac[n]=quick_pow(fact[n]),i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
	for (inv[0]=i=1;i<=n;++i) inv[i]=1LL*ifac[i]*fact[i-1]%mod;
}
inline void DFS(CI now=1)
{
	sz[now]=1; for (auto to:v[now]) DFS(to),sz[now]+=sz[to];
}
inline void work(CI x)
{
	tag[x]^=1;
	if (tag[x]) ans=1LL*ans*sz[x]%mod; else ans=1LL*ans*inv[sz[x]]%mod;
}
class Link_Cut_Tree
{
    private:
        struct splay
        {
            int ch[2],fa;
        }node[N];
        #define lc(x) node[x].ch[0]
        #define rc(x) node[x].ch[1]
        #define fa(x) node[x].fa
        inline void connect(CI x,CI y,CI d)
        {
            node[fa(x)=y].ch[d]=x;
        }
        inline int identify(CI now)
        {
            return rc(fa(now))==now;
        }
        inline bool isroot(CI now)
        {
            return lc(fa(now))!=now&&rc(fa(now))!=now;
        }
        inline void rotate(CI now)
        {
            int x=fa(now),y=fa(x),d=identify(now); if (!isroot(x)) node[y].ch[identify(x)]=now;
            fa(now)=y; connect(node[now].ch[d^1],x,d); connect(x,now,d^1);
        }
        inline void splay(int now)
        {
            for (int t;!isroot(now);rotate(now))
            t=fa(now),!isroot(t)&&(rotate(identify(now)!=identify(t)?now:t),0);
        }
        inline int findroot(int now)
        {
            while (lc(now)) now=lc(now); return now;
        }
    public:
        inline void link(CI x,CI y)
        {
            fa(x)=y;
        }
        inline void access(int x)
        {
            for (int y=0,t;x;x=fa(y=x))
            {
                splay(x); if (rc(x)) t=findroot(rc(x)),work(t);
                if (rc(x)=y) t=findroot(rc(x)),work(t);
            }
        }
        #undef lc
        #undef rc
        #undef fa
}LCT;
int main()
{
	RI i; for (scanf("%d%d",&n,&q),i=2;i<=n;++i)
	scanf("%d",&x),LCT.link(i,x),v[x].push_back(i);
	for (DFS(),init(n),ans=fact[n],i=1;i<=n;++i) ans=1LL*ans*inv[sz[i]]%mod;
	while (q--) scanf("%d",&x),LCT.access(x),printf("%d\n",ans);
	return 0;
}

爆搜,启动!

题目等价于找一条最长的链,需要满足这条链对应点集的导出子图对应的边只有链上的边

考虑用爆搜处理,用二元组 \((x,mask)\) 表示当前位于点 \(x\),之前链上的点集为 \(mask\)

每次转移枚举下一步走的点 \(y\),需要满足 \(x,y\) 间有边,且 \(mask\) 和 \(y\) 之间没边

根据经典复杂度分析,发现最坏情况下复杂度为 \(O(3^n\times n)\),可以通过

(话说 THU 很喜欢出这种爆搜然后证明复杂度正确的题,之前做某个 Regional 好像也遇到过)

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
typedef long long LL;
const int N=45;
int n,m,x,y,ans; LL g[N]; vector <int> v[N];
inline void DFS(CI now,const LL& pre=0,CI len=1)
{
    ans=max(ans,len);
    for (auto to:v[now]) if (((pre>>to)&1)==0&&(g[to]&pre)==0) DFS(to,pre|(1LL<<now),len+1);
}
int main()
{
    RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y); --x; --y;
        v[x].push_back(y); v[y].push_back(x);
        g[x]|=(1LL<<y); g[y]|=(1LL<<x);
    }
    for (i=0;i<n;++i) DFS(i);
    return printf("%d",ans),0;
}

祁神正在绝赞补题中,我直接开摆了


神秘 DP 题,完全没想到这样设状态

令 \(f_{l,r,c}\) 表示已知答案在 \(a_l\sim a_r\) 之间,且在 \(l\) 左侧的询问次数减去在 \(r\) 右侧的询问次数为 \(c\) 的最小代价

之所以这么设状态是因为我们考虑在最后得知询问结果时得到 \(x\) 对应的系数,在计算贡献时左右各一次询问会相互抵消,因此只要知道差值就能计算贡献了

转移的时候考虑枚举 \(y\in(a_k,a_{k+1}]\),最优的转移点显然就是让贡献尽量平均的点

但直接这么做复杂度是 \(O(n^3\times |c|)\) 的,通过观察可以发现 \(f_{l,r,c}\) 的决策点一定在 \(f_{l,r-1,c}\) 的决策点的右侧,因此可以用 two pointers 优化

同时根据题解中的分析,\(c\) 的值小于 \(\log n\)级别,因此最后总复杂度为 \(O(n^2\log n)\)

#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=1005,INF=1e18;
int n,a[N],f[N][N][85];
signed main()
{
    RI i,j,k; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld",&a[i]);
    for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=0;k<=80;++k) f[i][j][k]=INF;
    for (i=1;i<=n;++i) for (j=-40;j<=40;++j) f[i][i][j+40]=a[i]*j;
    for (RI l=n;l>=1;--l) for (k=1;k<80;++k)
    {
        int p=l+1; for (RI r=l+1;r<=n;++r)
        {
            while (p<=r)
            {
                int L=f[l][p-1][k-1],R=f[p][r][k+1];
                if (L==INF||R==INF) break;
                int val=(R-L+1)/2;
                if (val>a[p]) val=a[p];
                if (val<=a[p-1]) val=a[p-1]+1;
                int tmp=max(L+val,R-val);
                if (tmp<f[l][r][k]) f[l][r][k]=tmp,++p; else break;
            }
            --p;
        }
    }
    return printf("%lld",f[1][n][40]),0;
}

由于贡献只能往前移动不能往后,因此考虑从前往后把数一个个加进去

设当前加入了 \(a_i\),\(a_1\sim a_{i-1}\) 中的最小值为 \(M\),显然若 \(a_i>M\) 则从 \(a_i\) 换一个 \(1\) 给 \(M\) 一定不会让答案变劣

暴力重复以上过程,复杂度 \(O(n^2\times a_i)\)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=105,mod=998244353;
int t,n,a[N];
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
        for (i=2;i<=n;++i)
        {
            for (;;)
            {
                int mn=101,pos=-1;
                for (j=1;j<i;++j) if (a[j]<mn) mn=a[j],pos=j;
                if (a[i]<=mn) break;
                ++a[pos]; --a[i];
            }
        }
        int ans=1; for (i=1;i<=n;++i) ans=1LL*ans*a[i]%mod;
        printf("%d\n",ans);
    }
    return 0;
}

Postscript

每天打多校感觉都在坐牢,感觉心态要被打出问题了苦露西

标签:const,int,多校,times,2024,牛客,include,RI,define
From: https://www.cnblogs.com/cjjsb/p/18333270

相关文章

  • 2024“钉耙编程”中国大学生算法设计超级联赛(3) 1005 数论
    题意:分析:远看数论题,实则是道数据结构。记\(f_{i}\)表示\(r_{k}=i\)的方案数,\(g_{i}\)表示\(l_{1}=i\)的方案数,那么运用简单容斥,可得:\[ans_{x}=(\sum_{i=1}^{n}f_{i})-((\sum_{i=1}^{x-1}f_{i})+1)\times((\sum_{i=x+1}^{n}g_{i})+1)+1\]先考虑如何计算\(f_{i......
  • c语言笔记(2024.7.24)第三天
    常量与变量概念:·表面:程序运行过程中取值可以改变的数据·实际:变量其实代表了一块内存区域/单元/空间。变量名可视为该区域的标识。整个变量分为三部分:·变量名:这个只是变量的一个标识,我们借助变量名来存取数据。·变量空间/存储单元:这个就是内存中分配的一块用来存放......
  • 2024“钉耙编程”中国大学生算法设计超级联赛(1)1012并
    题目大意:给出n个矩形,求被k个矩形覆盖的面积的并集的期望,输出k为1-n的所一答案思路:由于是求期望所以是求出所有情况的和再除以可能的情况,每一种情况中的面积都由--同时被1个矩形覆盖,同时被两个矩形覆盖······同时被k个矩形覆盖组成,而且不难得出当k一定时,取被m个矩形覆盖的......
  • 【调试笔记-20240730-Linux-OpenWrt 23.05 安装 Docker 配置 bitnami/Wordpress-with-
    调试笔记-系列文章目录调试笔记-20240730-Linux-OpenWrt23.05安装Docker配置bitnami/Wordpress-with-NGINX实现微信用户在线注册登录文章目录调试笔记-系列文章目录调试笔记-20240730-Linux-OpenWrt23.05安装Docker配置bitnami/Wordpress-with-NGINX实现......
  • 2024-7-30 信友队模考总结
    开考这次的题目看着比较简单,第一题一眼前缀和,第二题是双指针,三四题不很一眼,感觉可以冲300pts。果然T1直接秒掉,J组难度。开写第二题感觉是双指针,而且也很有单调性,但是怎么实现并没有一下想出来,写了大概10min过了样例和自测,但是观摩的时候发现假了,我写的是伪双指针,\(\math......
  • 企业常用源代码加密软件,2024五款源代码加密软件推荐
    在现代企业中,源代码是核心资产之一,其安全性对企业的竞争力和创新能力至关重要。为了防止代码泄露和未经授权的访问,许多企业选择使用源代码加密软件。以下是2024年五款值得推荐的源代码加密软件,为企业提供可靠的安全保障。1.安秉源代码加密软件安秉源代码加密软件是一款专为......
  • 2024夏令营CTF部分wp
    misc前面几题基本来源于这篇文章>https://blog.csdn.net/qq_45894840/article/details/128346180?spm=1001.2014.3001.5502算是misc的入门级题目,就不多说了1.easy_stego_1是盲水印分离的题目首先拿到题目附件>http://nnd.edaker.com:8999/directlink/2/misc_easy_stego_1.p......
  • 2024 年求职不易,有没有什么效率高的求职方法?
    对于很多打工人来说,今年过得并不容易,不管是打工还是求职,都感觉艰难许多。市场竞争力变大,让许多打工人都感受到了浓浓的“求职焦虑”。对于应届生而言,今年更是具有挑战性的一年,毕业人数量高达1179万人,又创历史新高,毕业生的增多,就意味着就业竞争压力更大…在这样的就业形势下,最......
  • 【往届会后三个半月内EI检索 | EI会议征稿 】第四届物联网与机器学习国际学术会议(IoTM
     第四届物联网与机器学习国际学术会议(IoTML2024)20244th InternationalConferenceonInternetofThingsandMachineLearning重要信息大会时间:2024年8月9-11日         大会地点:中国-南昌        大会官网:www.iotml.cn   会......
  • 2024口碑最好五大骑行耳机精选,实测体验在线分享!
    作为有着多年骑行经验的数码博主,我深刻的明白,在骑行过程中,选择一款合适的耳机至关重要,一款合适的骑行耳机不仅可以增加骑行中的体验,还能保证骑行中的安全,骨传导耳机凭借不入耳佩戴更健康,以及开放式听音等优点成为众多骑行爱好者的首要选择。但随着骨传导耳机热度增加,市面上开始......