隐藏页面特效

2024牛客暑期多校训练营5

1|0Preface


坐牢,爽!

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

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

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

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

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


2|0


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

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

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

1134 2234 5578 6678
1156 2356 2344

因此我们就有了 2×2 的构造单元和 2×3 的构造单元,简单讨论后发现用这两种单元可以构造除了 1×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; }

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

4|0


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

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

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

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

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

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

5|0


爆搜,启动!

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

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

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

根据经典复杂度分析,发现最坏情况下复杂度为 O(3n×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; }

6|0


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


7|0


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

fl,r,c 表示已知答案在 alar 之间,且在 l 左侧的询问次数减去在 r 右侧的询问次数为 c 的最小代价

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

转移的时候考虑枚举 y(ak,ak+1],最优的转移点显然就是让贡献尽量平均的点

但直接这么做复杂度是 O(n3×|c|) 的,通过观察可以发现 fl,r,c 的决策点一定在 fl,r1,c 的决策点的右侧,因此可以用 two pointers 优化

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

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

8|0


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

设当前加入了 aia1ai1 中的最小值为 M,显然若 ai>M 则从 ai 换一个 1M 一定不会让答案变劣

暴力重复以上过程,复杂度 O(n2×ai)

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

9|0Postscript


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


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/18333270.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(451)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示