20251030模拟赛
T1
小模拟题,场切了。
T2
有一个只含 \(0,1\) 的字符串 \(s\),在其中选择两个子串 \(t1\),\(t2\),要求 \(t1\oplus t2\) 最大并输出这个二进制表示。
你说的对,但 \(1\le |s|\le 10^7\)。
首先前导 \(0\) 是没用的,可以全部去掉。
然后第一个子串肯定是取去掉前导 \(0\) 的串。
第二个字符串要看出现的第一个 \(0\),设从这个位置到结尾的子串为 \(t\),要找一个长度与 \(t\) 相同的子串使得它异或 \(t\) 最大。
场上用的是将所有长度符合的字串放进 trie 树来找,时间复杂度 \(\mathcal O(n^2)\)。
但其实可以直接贪心做到 \(\mathcal O(n)\)。
其实我也不是很理解,这里直接放官解:
再考虑 \(t\) 右边最长的相邻的一整块 \(0\),记它们的右边界为 \(v\),在理想情况下我们让 \(t\) 中的 \(0\) 都能变
成 \(1\),同时利用 \(t~v\) 这些 \(0\) 让 \(v\) 右边的第一个 \(1\) 不受影响。可以发现这种取法是唯一的并且一定最
优,只要让 \(max(\text{第一个} 1 \text{的位置},t-(t-v+1))\) 作为第一个串的开头就行了。
#include<bits/stdc++.h>
#define N 10000005
#define getc getchar_unlocked
#define putc putchar_unlocked
using namespace std;
int n,a[N];
int main(){
int T;scanf("%d",&T);
while(T--){
int u=-1,v=-1,t,res;
scanf("%d",&n);
for(int i=1;i<=n;i++){
char c;
do c=getc();
while(c!='0'&&c!='1');
a[i]=c=='1';
if(a[i]&&u==-1) u=i;
if(!a[i]&&u!=-1&&v==-1) v=i;
}
if(u==-1){puts("0");continue;}
if(v==-1){
for(int i=u;i<n;i++) putc('1');
putc(u==1?'0':'1');
puts("");continue;
}
for(t=v;!a[t+1];t++);
res=max(u,(v<<1)-t-1);
for(int i=u;i<=n;i++){
if(i<v) putc(a[i]?'1':'0');
else putc((a[i]^a[res+i-v])?'1':'0');
}
puts("");
}
return 0;
}
T3
序列中有 \(n\) 个值域在 \([1,m]\) 的正整数,每次可以将形如 \((x,x,x)\) 和 \((x,x+1,x+2)\)(\(x\) 为正整数)的三个数合并成一组,求将整个序列全部合并成 \(\dfrac{n}{3}\) 组的方案数模 \(10^9+7\)。
自认为比 T2 简单。
容易发现合并与位置无关,于是记 \(a_i\) 为 \(i\) 的出现次数。
可以想到每个数 \(x\) 只能与 \([x-2,x+2]\) 的数进行合并,也就是说考虑它的合并时只会影响前后 \(2\) 个数,可以 DP。
设 \(f_{i,j,k}\) 为当前考虑数 \(i\) (\(i\) 还没有合并,\(i-2\) 及比它小的的已经全部合并完),\(i\) 剩下 \(j\) 个,\(i-1\) 剩下 \(k\) 个的方案数。
转移方程为:
直接做的话空间是 \(\mathcal O(n^2m)\) 的,滚动一下第一维变成 \(\mathcal O(n^2)\)
注意转移方程中要用到 \(j+3\) 和 \(k+a_i-j\),所以要从大往小枚举。
滚动了数组记得清空。
时间复杂度 \(\mathcal O(\sum_{i=1}^n a_i)\le \mathcal O(n^2)\)。
#include<bits/stdc++.h>
#define N 5005
using namespace std;
const int mod=1000000007;
int n,m,a[N],f[2][N][N];
void upd(int &x,int y){
x+=y;
while(x>=mod) x-=mod;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,x;i<=n;i++)
scanf("%d",&x),a[x]++;
f[0][0][0]=1;
for(int i=1;i<=m;i++){
bool x=i&1,y=i&1^1;
for(int j=a[i];j>=0;j--){
int v=a[i]-j;
for(int k=min(a[i-1]-v,j);k>=0;k--)
upd(f[x][j][k],f[y][k+v][v]);
}
for(int j=a[i]-3;j>=0;j--)
for(int k=min(a[i-1],j);k>=0;k--)
upd(f[x][j][k],f[y][j+3][k]);
if(i==1) f[0][0][0]=0;
else
for(int j=0;j<=a[i-1];j++)
for(int k=min(a[i-2],j);k>=0;k--)
f[y][j][k]=0;
}
printf("%d",f[m&1][0][0]);
return 0;
}