L.最小括号串

数组操作 #贪心

题目

思路

感谢Leratiomyces大佬赛时的提示,否则估计还一直签不了到()

首先,贪心地构造出最优情况:数组左半部分全是(,右半部分全是),随后通过判断给定的区间中是否包含(来决定是否调整当前序列。

对给定的区间按照左端点降序排序,这样就可以从右往左有序遍历所有区间。

设置两个指针\(id_{l},id_{r}\):

  • \(id_{l}\)指向未被调换的(中最右的(的下标
  • \(id_{r}\)指向经过调换后的(中最左的(的下标

从右往左有序遍历所有区间:

  • 如果当前区间右端点\(r\)在\(id_{r}\)左侧,那么说明当前区间\([l,r]\)中一定没有(,因此需要调度一个(前往\(l\)位置(尽量保证字典序小),即\(swap(ans[\ id_{l}\ ],ans[\ l\ ])\)
  • 调度前需要注意\(id_{l}\)是否为0,即序列左端是否还剩余(。如果\(id_{l}==0\)而仍然需要调度(,那么必然是不合法的,输出-1

最后不要忘了遍历整个序列判断是否会存在)(的非法情况

代码实现

#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<unordered_set>
#include<queue>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i++)
#define per(i, a, b) for(ll i = (a); i >= (b); i--)
#define mid ((l+r)>>1)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
const int N=1e5+5;
int n,m;
struct lr{
int l,r;
bool operator<(const lr&t)const{
return l>t.l;
}
};
void solve() {
cin>>n>>m;
n*=2;
vector<lr>b(m+1);
vector<char>ans(n+1);
rep(i,1,m){
int l,r;cin>>l>>r;
b[i]={l,r};
}
rep(i,1,n){
if(i<=n/2)ans[i]='(';
else ans[i]=')';
}
sort(b.begin()+1,b.begin()+1+m);
int idl=n/2,idr=n+1;
rep(i,1,m){
int l=b[i].l,r=b[i].r;
if(r<idr){
if(idl<1){
cout<<-1<<'\n';return;
}
swap(ans[idl],ans[l]);
idr=l;
idl--;
}
}
int cntl=0,cntr=0;
rep(i,1,n){
if(ans[i]=='(')cntl++;
else cntr++;
if(cntr>cntl){
cout<<-1<<'\n';return;
}
}
rep(i,1,n)cout<<ans[i];
cout<<'\n';
} int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}

C.

数学 #组合数 #斯特林数

题目

思路

提供一种纯数学的方法:

设\(S_{n}^m\)为长度为\(n\)的所有排列中\(size\)为\(m\)的数量

易知\(\sum_{i=1}^nS_{n}^i=n!\),即排列数\(A_{n}^n\)

接下来可以找出他的递推:

  • 当数字\(n\)不位于排列的最后一位时,\(stack\)的\(pop\)功能必定会将其\(pop\)掉,即数字\(n\)不产生贡献。数字\(n\)不位于最后一位的可能有\(n-1\)种,那么就可以将此时的情况等价于\(长度为n-1的所有排列中size为m的数量\),即\((n-1)\times S_{n-1}^m\)
  • 当数字\(n\)位于排列的最后一位时,其必然对\(size\)产生\(1\)的贡献,那么就可以将此时的情况等价于\(长度为n-1的所有排列中size为m-1的数量\),即\(S_{n-1}^{m-1}\)

    综上:
\[S_{n}^m=(n-1)\times S_{n-1}^m+S_{n-1}^{m-1}
\]

则答案所求即:

\[\sum_{i=1}^n i^3·S_{n}^i
\]

为了求解答案,我们设\(sum_{j}[n]=\sum_{i=1}^ni^j·S_{n}^i\),其中:

  • \(sum_{0}[n]=\sum_{i=1}^nS_{n^i}=n!\)
  • 所求答案即\(sum_{3}[n]\)

接下来我们来研究\(sum_{j}\)之间的递推:

\[\begin{align}
sum_{1}[n]&=\sum_{i=1}^ni·S_{n-1}^i\\ \\
&带入递推式S_{n}^m=(n-1)\times S_{n-1}^m+S_{n-1}^{m-1}:\\ \\
&=\sum_{i=1}^ni·[\ (n-1)S_{n-1}^i+S_{n-1}^{i-1}]\\ \\
&=(n-1)\sum_{i=1}^ni·S_{n-1}^i+\sum_{i=1}^ni·S_{n-1}^{i-1}\\ \\
&=(n-1)sum_{1}[n-1]+\sum_{i=0}^{n-1}(i+1)·S_{n-1}^i\\ \\
&=(n-1)sum_{1}[n-1]+\sum_{i=1}^{n-1}i·S_{n-1}^i+\sum_{i=1}^{n-1}S_{n-1}^i\\ \\
&=(n-1+1)sum_{1}[n-1]+sum_{0}[n-1]\\ \\
sum_{1}[n]&=n·sum_{1}[n-1]+sum_{0}[n-1]
\end{align}
\]

因此我们可以完全类比推导过程得出\(sum_{j}[n]\)的公式:

\[\begin{align}
&sum_{j}[n]=\sum_{i=1}^ni^j·S_{n}^i\\ \\
&=\sum_{i=1}^ni^j·[(n-1)S_{n-1}^i+S_{n-1}^{i-1}]\\ \\
&=(n-1)sum_{j}[n-1]+\sum_{i=1}^ni^j·S_{n-1}^{i-1}\\ \\
&=(n-1)sum_{j}[n-1]+\sum_{i=0}^{n-1}(i+1)^j·S_{n-1}^i\\ \\
&=(n-1)sum_{j}[n-1]+\sum_{i=1}^{n-1}\sum_{k=0}^jC_{j}^k·i^k·S_{n-1}^i\\ \\
&=(n-1)sum_{j}[n-1]+\sum_{k=0}^jC_{j}^k\sum_{i=1}^{n-1}i^k·S_{n-1}^i\\ \\
sum_{j}[n]&=(n-1)sum_{j}[n-1]+\sum_{k=0}^jsum_{k}[n-1]
\end{align}
\]

由此我们可以在\(o(n)\)的复杂度下递推得到\(sum_{3}[n]\)

代码实现

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cmath>
#include<unordered_map>
#include<set>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
const int MOD = 998244353;
const int N = 5e5 + 5; ll ans[N], sum3[N], sum2[N], sum1[N], sum0[N]; void precompute() {
sum0[1] = 1;sum1[1] = 1;sum2[1] = 1;sum3[1] = 1;ans[1] = 1;
for (int n = 2; n < N; ++n) {
sum0[n] = (n * sum0[n-1]) % MOD;
sum1[n] = (n * sum1[n-1] + sum0[n-1]) % MOD;
sum2[n] = (n * sum2[n-1] + 2 * sum1[n-1] + sum0[n-1]) % MOD;
sum3[n] = (n * sum3[n-1] + 3 * sum2[n-1] + 3 * sum1[n-1] + sum0[n-1]) % MOD;
ans[n] = sum3[n];
}
} int main() {
ios::sync_with_stdio(false);
cin.tie(0);
precompute();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
cout << ans[n] << '\n';
}
return 0;
}

K. 最大gcd

数学 #gcd #差分

题目

思路

已知gcd具有特殊性质:

\[gcd(a_{1},a_{2},\dots,a_{n})=gcd(a_{1},a_{2}-a_{1},a_{3}-a_{2},\dots,a_{n}-a_{n-1})
\]

设差分数组\(b[N],b[i]=a[i]-a[i-1]\)

则对于区间\([l,r]\)内每个数都加\(k\)相当于对\(b[l]+=k,b[r+1]-=k\)

  • 首先讨论整个数组都\(+k\)的情况:

    • 操作\(b[1]+=k\)即可(\(b[r+1]\)不存在)
    • 此时的gcd为\(gcd(b_{1}+k,b_{2},\dots,b_{n})\)
    • 必定存在\(k\)使得\(gcd(b_{2},b_{3},\dots,b_{n})|(b_{1}+k)\)
    • 因此该情况的gcd记为\(g_{1}=gcd(b_{2},b_{3},\dots,b_{n})\)
  • 接下来讨论局部\(+k\)的情况:
    • 由于是局部操作,所以\(b_{1},b_{n}\)这两个的其中一个必定不会被\(+k\),因此枚举\(b_{1},b_{n}\)的所有因数\(f\)
    • 若所有的\(b_{i}\)都为\(f\)的倍数,那么当前的\(f\)一定是合法的gcd
    • 若只有一个\(b_{i}\)不是\(f\)的倍数,那么一定存在一个\(k\)使得\(f|(b_{i}+k)\),当前的\(f\)也是合法的
    • 若超过两个\(b_{i}\)不是\(f\)的倍数,那么无论怎么操作都不可能使得整个数组的gcd为\(f\)
    • 若只有两个\(b_{i}\)不是\(f\)的倍数,那么需要特殊判断。在此提供两种判断方法:
      • 方法一:

        • 设不是\(f\)的两个数为\(b_{1},b_{2}\),则\(b_{1}=t_{1}\times f+b_{1}\%f,b_{2}=t_{2}\times f+b_{2}\%f\)
        • 令\(k=b_{1}\%f\),则\(b_{1}-k=t_{1}\times f\)为\(f\)的倍数;
        • \(b_{2}+k=t_{2}\times f+b_{1}\%f+b_{2}\%f\)要为\(f\)的倍数,则必有\((b_{1}\%f+b_{2}\%f)\%f=0\)
        • 因此可以通过判别式\((b_{1}\%f+b_{2}\%f)\%f=0\)来确定两个\(b_{i}\)是否合法
      • 方法二:
        • 设不是\(f\)的两个数为\(b_{1},b_{2}\)且\(b_{1}-k,b_{2}+k\)为\(f\)的倍数,则有\(b_{1}\equiv k\pmod{f},b_{2}\equiv(-k)\pmod{f}\)
        • 两式相加得\((b_{1}+b_{2})\equiv0\pmod{f}\),即\((b_{1}+b_{2})\%f=0\)
        • 因此可以通过判别式\((b_{1}+b_{2})\%f=0\)来判断两个\(b_{i}\)是否合法

特别需要注意的是,由于差分可能会导致负数,所以gcd函数返回的时候需要加绝对值

这与给传入的变量加绝对值是完全不一样的操作!

代码实现

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cmath>
#include<unordered_map>
#include<set>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
//#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
const ll inf = 1e7 + 5;
// #define int ll
const int N=1e5+5; ll gcd(ll a,ll b){
if(!b)return abs(a);
return gcd(b,a%b);
} int a[N],b[N];
void eachT() {
int n;cin>>n;
bool same=1;
int g1;
rep(i,1,n){
cin>>a[i],b[i]=(a[i]-a[i-1]);
if(i==2)g1=b[2];
if(i>=3)g1=gcd(g1,(b[i]));
if(i>=2&&a[i]!=a[i-1])same=0;
}
if(same){
cout<<0<<'\n';return;
}
if(n==2){
cout<<max(a[1],a[2])<<'\n';return;
}
set<int>fac;
for(int i=1;i*i<=a[1];i++){
if(a[1]%i==0)fac.insert(i),fac.insert(a[1]/i);
}
for(int i=1;i*i<=a[n];i++){
if(a[n]%i==0)fac.insert(i),fac.insert(a[n]/i);
}
int cnt,ans=g1;
for(auto&f:fac){
int mod=0;
cnt=0;
rep(i,2,n)if(b[i]%f!=0)cnt++,mod+=b[i]%f;
if(cnt<=1){
ans=max(ans,f);
}
if((cnt==2)&&(mod%f==0)){
ans=max(ans,f);
}
}
cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll t = 1;
cin >> t;
while (t--) { eachT(); }
}

D.漂亮矩阵

数学 #组合数 #广义二项式定理 #FFT

题目

思路

设\(B_{i,j}=A_{i,j+1}-A_{i,j}\),则有\(B_{i,j}\geq B_{i+1,j}\)且\(\sum_{j=1}^nB_{i,j}\leq m\)

因此有\(\sum_{j=1}^nB_{i+1,j}\leq \sum_{j=1}^nB_{i,j}\leq \sum_{j=1}^nB_{1,j}\leq m\)

所以只需要满足\(\sum_{1,j}^nB_{1,j}\leq m\)且每列的\(B\)值单调不增即可

设\(B_{1,i}=x\),一列共有\(n\)个元素,求这一列开头为\(x\)且后续单调不增的所有情况数:

  • 假设有\(x\)块隔板\(l_{i},i\in[0,x]\),若\(l_{i-1}\)到\(l_{i}\)间有\(k\)个数,则令这\(k\)个数为\(i\)
  • 由于开头已经固定为\(x\),只能对剩下的\(n-1\)个数进行操作,\(n-1\)个数提供\(n-1\)个空位,再加上\(x\)个隔板自己的位置,相当于一共\(x+n-1\)个空位中放入\(x\)个隔板,即\(C_{x+n-1}^x\)

接下来利用生成函数来构造总和不超过\(m\)的所有情况数:

  • 构造函数\(F(x)=C_{0+n-1}^0x^0+C_{1+n-1}^1x^1+\dots+C_{m+n-1}^mx^m+\dots=\sum_{i=0}^\infty C_{i+n-1}^ix^i\)
  • 构造多项式\(F(x)·F(x)·\dots·F(x)=F(x)^{n-1}\),相当于除了第一列以外的\(n-1\)列的所有情况。多项式\(F(x)^{n-1}\)中的前\(m\)项的系数之和即为总和不超过\(m\)的所有情况数

此时可以用\(FFT\)来快速得出答案,复杂度\(o(n\log n)\)

但是其实还可以用广义二项式定理进一步化简:

广义二项式定理实际上就是将组合数的定义域拓宽到了整个实数域

\[\begin{align}
&F(x)=\sum_{i=0}^\infty C_{i+n-1}^ix^i=(1-x)^{-n}\ \ \ (广义二项式定理)\\ \\
&F(x)^{n-1}=(1-x)^{-n(n-1)}\\ \\
&令t=n(n-1) \\ \\
&则F(x)^{n-1}=(1-x)^{-t} =\sum_{i=0}^\infty C_{i+t-1}^ix^i=\sum_{i=0}^\infty C_{i+t-1}^{t-1}x^i\\ \\
&则其前m项的系数和为\sum_{i=0}^mC_{i+t-1}^{t-1}=C_{m+t}^t\\ \\
&即求 \frac{(m+t)!}{t!·m!}= \frac{(m+t)(m+t-1)···(t+1)}{m!}
\end{align}
\]

分子可以\(o(m)\)暴力算出,分母可以\(o(m)\)预处理逆元算出,总复杂度为\(o(m)\)

代码实现

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cmath>
#include<unordered_map>
#include<set>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
//#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
const ll inf = 1e7 + 5;
// #define int ll
const int N=1e5+5; ll mod=998244353;
ll qpow(ll a, ll b) {
a %= mod; ll res = 1;
while (b) {
if (b % 2) { res *= a, res %= mod; }
a *= a, a %= mod, b /= 2;
}
return res%mod;
}
vector<ll>a, inv;
void inv0(ll len) {
a.resize(len + 1), inv.resize(len + 1);
a[0] = 1, inv[0] = 1;
rep(i, 1, len) {
a[i] = a[i - 1] * i % mod;
inv[i] = qpow(a[i], mod - 2);
}
} void eachT() {
ll n,m;cin>>n>>m;
ll ans=1;
rep(i,1,m){
ans*=(n*(n-1)+i)%mod;
ans%=mod;
}
ans*=inv[m];
ans%=mod;
cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
inv0(5e5);
ll t = 1;
//cin >> t;
while (t--) { eachT(); }
}

2025牛客多校第六场 D.漂亮矩阵 K.最大gcd C.栈 L.最小括号串 个人题解的更多相关文章

  1. 牛客多校第六场 C Generation I 组合数学 阶乘逆元模板

    链接:https://www.nowcoder.com/acm/contest/144/C来源:牛客网 Oak is given N empty and non-repeatable sets whi ...

  2. 牛客多校第六场 J Heritage of skywalkert 随即互质概率 nth_element(求最大多少项模板)

    链接:https://www.nowcoder.com/acm/contest/144/J来源:牛客网 skywalkert, the new legend of Beihang University ...

  3. 牛客多校第六场-H-Pair

    链接:https://ac.nowcoder.com/acm/contest/887/H来源:牛客网 题目描述 Given three integers A, B, C. Count the numb ...

  4. 2019牛客多校第四场 I题 后缀自动机_后缀数组_求两个串de公共子串的种类数

    目录 求若干个串的公共子串个数相关变形题 对一个串建后缀自动机,另一个串在上面跑同时计数 广义后缀自动机 后缀数组 其他:POJ 3415 求两个串长度至少为k的公共子串数量 @(牛客多校第四场 I题 ...

  5. 同构图+思维构造——牛客多校第六场E

    考的其实是同构图的性质: 1.同构图的顶点数,边数相等 2.同构图通过点的映射后邻接矩阵相同 这篇博客讲的很好https://www.jianshu.com/p/c33b5d1b4cd9 本题还需要一 ...

  6. 2018牛客多校第六场 G.Pikachu

    题意: 给出一棵n个点的树,每条边有边权.对这个树加边变成一个完全图.新加的边的权值为边上两点在树上的距离.求完全图上任意两点的最大流之和. 题解: 一共有C(n,2)个点对.假设当前求s到t之间的最 ...

  7. 2018牛客多校第六场 I.Team Rocket

    题意: 给出n个区间和m个点(点按顺序给出且强制在线).每个区间只会被第一个他包含的点摧毁.问每个点能摧毁多少个区间以及每个区间是被哪个点摧毁的. 题解: 将n个区间按照左端点排序,然后用vector ...

  8. 牛客多校第六场C

    一个数很大,并不能预处理,所以要进行公式变换,存前一个的值就好 #include <bits/stdc++.h> using namespace std; typedef long lon ...

  9. Palindrome Mouse(2019年牛客多校第六场C题+回文树+树状数组)

    目录 题目链接 题意 思路 代码 题目链接 传送门 题意 问\(s\)串中所有本质不同的回文子串中有多少对回文子串满足\(a\)是\(b\)的子串. 思路 参考代码:传送门 本质不同的回文子串肯定是要 ...

  10. 2019牛客多校第六场 B - Shorten IPv6 Address 模拟

    B - Shorten IPv6 Address 题意 给你\(128\)位的二进制,转换为十六进制. 每\(4\)位十六进制分为\(1\)组,每两组用一个\(":"\)分开. 每 ...

随机推荐

  1. 一个大对象引起的血案,GC的踩坑实录

    背景:   问题: 有个渠道支付服务,负责与所有支付相关服务进行交互,包括 渠道下单支付,渠道成功通知,渠道的对账等 服务4台机,平时跑的都很稳定,通过thrift进行对外提供服务,且平时并未发现访问 ...

  2. JAVA基础-跳出循环的4种方式

    摘要:介绍4种跳出循环的方式,尤其是其中的break 标签,有时候真的会成为工作中的秘密武器.   在实际编程中,有时需要在条件语句匹配的时候跳出循环.在 Java 语言里,由关键词 break 和 ...

  3. Golang遍历空数组实现指定次数的循环

    var nums [10][0]int for range nums { fmt.Println("这里循环输出十次") } golang 的for遍历还是比较简单的,为什么还要用 ...

  4. 「Log」2023.8.24 小记

    序幕 \(\texttt{7:20}\):才到校,昨天调题整半夜去了,没想到这么晚来的人也少. 按惯例整理博客. 补题,补串串. \(\color{blueviolet}{P2444\ [POI200 ...

  5. 你应该懂的AI大模型(三)之 RAG

    从本篇开始笔者会尽量多使用一些英文缩写和单词,不是笔者为了装X,是为了大家在后面遇到的时候不至于被别人装到. 一.什么是RAG 1.1 大模型的局限性 大模型的知识不是实时的,比如现在<藏海传& ...

  6. Lustre 与 JuiceFS :架构设计、文件分布与特性比较

    在 AI 模型训练.高性能计算等对 I/O 敏感的场景中,底层文件系统的架构和性能将直接影响训练效率.资源利用率与整体成本. Lustre 作为传统高性能文件系统,以极致性能著称:而 JuiceFS ...

  7. 二、Linux基本应用工具

    1.系统文件共享(网络) 通过网络文件共享协议(例如 SMB 或 NFS)来完成Ubuntu下的文件夹共享给 Windows 1.Samba 实现共享 安装samaba sudo apt update ...

  8. FastGithub 使用遇到问题

    火狐浏览器 https://blog.csdn.net/weixin_33847182/article/details/86129219 因 HTTP 严格传输安全(HSTS)机制无法打开网页 1.打 ...

  9. 2025 HarmonyOS 创新赛正式启动,百万大奖等你挑战!

    2025年6月21日-2025年10月底,一年一度的HarmonyOS创新赛正式启幕!现已面向所有开发者开放报名通道. 这是一场鸿蒙生态面向开发者领域的顶级开发赛事,你可以尝试: 1.多种特性,抢先尝 ...

  10. 利于puppeteer获取网络资源的直链

    背景 比如我想使用curl 或者 页面按钮点击直接下载个网盘资源,那就会出现问题. 因为目前各大网盘给的分享链接都是一个页面,而且大部分还都做了防盗机制,你无法简单的获取真实下载连接! 但是我们可以利 ...