不会的好题总结
1. 天天爱前缀和
\(n(n\le4000)\) 长的序列 \(a_i(a_i<2^{12})\),你需要选择 \(a_i\) 的一个非空子序列,满足子序列中不存在连续四个数异或和为 \(s(s<2^{12})\),求方案数,\(a_i\) 互不相同。
题解:
存后三个数的暴力略。
\(f_{x,y}\):从左往右选数,当前选的最后一个数在 \(x\),选的倒数第二个数在 \(y\) 的合法方案数。
这时候我们不得不思考我们拼接是否合法的问题,首先 \(x,y,z,i\) 不合法我们用了一下容斥,但是容斥减去的东西是对的吗?我们减去了 \(x,y,z,i\) 不合法,但是却用了 \(f_{i,z}\),\(f\) 只保证了 \(z\) 前合法,而 \(i,z\) 拼接上 \(y\) 这一步合法吗?我们发现实际上 \(a_x\oplus a_y\oplus a_z\oplus a_i=s\) 与 \(a_i\) 互不相同的已经限制了 \(y\) 前合法,而到 \(x\) 第一次不合法。
现在到了维护时间!这是我喜欢这题的地方,他是很好的前缀和练习题。
总之就是考虑新的 \(f_{x,y}\) 对这三个数组的影响。
\(qz_x\) 可以直接处理,因为转化为了一维偏序。
容易发现在枚举 \(x\) 的时候处理 \(t_{x,m}\),现在复杂度还是很炸。
trick:增量处理含义有偏序的数组。
其中
因为枚举 \(i,p,m\) 是冗杂的,且对 \(i,p\) 限制相对 \(m\) 更紧密,然后发现只枚举 \(i<p\) 就可以更新 \(d_{p,m}\) 数组,也转化为了一维偏序。
总结:
此题允许 \(O(n^2)\),所以一维偏序直接做的时间复杂度是对的,所以化到一维就好了。(最底层的是每个形如 \(j<i\) 的 \(\{j,i\}\) 组合只会贡献一次,但是我们每次是用刚出来的 \(f_{x,j}\) 去贡献)
代码:
#include<bits/stdc++.h>
using namespace std;
const int QAQ=4100,mo=998244353;
int n,m,s,a[QAQ],f[QAQ][QAQ],d[QAQ][QAQ],qz[QAQ],t[QAQ][QAQ],ans;
#define jia(x,y) x=((x)+(y))%mo;
signed main()
{
cin>>n>>m>>s;
for(int x=1;x<=n;x++)
{
cin>>a[x],f[x][0]=1;
for(int y=0;y<x;y++)
{
if(y)
f[x][y]=(qz[y]-t[y][a[x]^a[y]^s]+mo)%mo,
jia(d[x][a[y]^a[x]],f[x][y]);
jia(qz[x],f[x][y]);
}
for(int i=0;i<(1<<m);i++) t[x][i]=(t[x-1][i]+d[x-1][i])%mo;
ans=(ans+qz[x])%mo;
}
cout<<ans;
return 0;
}
2. 天天爱计数
给定 \(n,m,k,n\le 5×10^7\),\(m\) 为字符集大小,问有多少长为 \(n\) 的字符串满足不存在长度为 \(2k\) 的连续子串使得前一半和后一半相同。对 \(998244353\) 取模。
题解:
\(f_i\):\([1,i]\) 合法方案数。
\(f_i=f_{i-1}×m-不合法方案数\)
不合法方案数是 \(f_{i-k}\) 吗?
这个和第一题一样,我们直接使用 \(f_{i-k}\) 是不可以的,因为我们相当于没有告诉 DP 数组 \([i-k+1,i]\) 的情况。
那么我们思考一下什么时候 \([i-k+1,i-1]\) 会使得 \(f_{i-k}\) 本身不合法(当然我们必须在第一次不合法的时候计数,所以我们需要使得 \(f_{i-k}\) 合法,才能构造不合法的字符串)。
推一下性质:
手玩一下临界情况,发现 \([i-k+1,i-1]\) 始终包含 \(i-k\) 和 \(i-2*k\) ,所以我们钦定这两个数不相等即可,同时相等也必然不合法。
所以不合法方案数是 \((m-1)f_{i-k-1}\)。
注意前 \(2k\) 预处理。