1001 Rikka with Candies
因为 i mod j=i−j⌊ji⌋,因此可以枚举 j 和 k=⌊ji⌋。这时相当与把 A 中区间 [kj,(k+1)j)中的所有数依次加到 w 的 [0,j) 区间上。
因为模 2 意义下的加法可以用异或实现,因此可以用压位来优化,这样时间复杂度为 ∑i=1nin×(32+32i),这约是 32n2。
注意因为 bitset 不支持提取区间操作,因此需要手写压位。如果不手写也可以用分块的方法来达到同样的复杂度但是常数比手写 bitset 大很多,可能需要一些常数优化才能通过时限。
题解2:#include <bits/stdc++.h>
using namespace std;
const int maxn = 50000 + 10 ;
bitset<maxn> cnt,cnt2,tmp;
int a[maxn],b[maxn],k[maxn];
struct LX{
int p;
int k;
bool operator < (const LX & b) const{
return k < b.k;
}
}x[maxn];
int ans[maxn];
int B[maxn];
int n,m,q;
int main()
{
int kase;scanf("%d",&kase);
while(kase--){
memset(B,0,sizeof(B));
cnt.reset();
cnt2.reset();
tmp.reset();
scanf("%d%d%d",&n,&m,&q);
for(int i = 1;i <= n;i++){
scanf("%d",&a[i]);
cnt[a[i]] = cnt[a[i]] ^ 1;
}
for(int i = 1;i <= m;i++){
scanf("%d",&b[i]);
B[b[i]] ^= 1;
}m = 0;
for(int i = 0;i <= 50000;i++) if(B[i] == 1) b[++m] = i;
for(int i = 1;i <= m;i++){
for(int j = 0;j < maxn;j+=b[i]){
cnt2[j] = cnt2[j] ^ 1;
}
}
for(int i = 1;i <= q;i++){
scanf("%d",&x[i].k);x[i].p = i;
}
sort(x + 1,x + q + 1);
int p = 1,last = 0;;
for(int i = 1;i <= q;i++){
int j = x[i].k;
if(i > 1 && x[i].k == x[i - 1].k) {ans[x[i].p] = last;continue;}
while(b[p] <= j) {
for(int jj = 0;jj < maxn;jj+=b[p]){
cnt2[jj] = cnt2[jj] ^ 1;
}
p++;
}
while(p <= m && b[p] == b[p + 1]) p += 1;
tmp = cnt2 << j;
ans[x[i].p] = (tmp & cnt).count() % 2;
last = ans[x[i].p];
}
for(int i = 1;i <= q;i++) {printf("%d\n",ans[i]);}
}
return 0;
}
1002 Rikka with String
如果没有反对称串的限制,直接求一个长度为 L 的 01 串满足所有给定串都出现过,那么是一个经典的 AC 自动机的问题,状态 f[i][j][S] 表示长度为 i,目前在 AC 自动机的节点 j 上,已经出现的字符串集合为 S 的方案数,然后直接转移即可,时间复杂度 O(2nL∑∣s∣)。
然后如果不考虑有串跨越中轴线,那么可以预处理所有正串的 AC 自动机和所有反串(即原串左右翻转)的 AC 自动机,然后从中间向两边 DP,每一次枚举右侧下一个字符是 0 还是 1,那么另一侧一定是另外一个字符。状态 f[i][j][k][S] 表示长度为 2i,目前右半边在正串 AC 自动机的节点 j 上,左半边的反串在反串 AC 自动机的节点 k 上,已经出现的字符串集合为 S 的方案数,然后直接转移,时间复杂度 O(2nL(∑∣s∣)2)。
现在考虑有串跨越中轴线,可以先爆枚从中间开始左右各 max∣s∣−1 个字符,统计出哪些串以及出现了。对于之后左右扩展出去的字符来说,肯定没有经过的它们的字符串跨越中轴线,因此可以以爆枚的结果为 DP 的初始值,从第 max∣s∣ 个字符开始 DP。
1006 Rikka with Graph
考虑贪心地一条一条边添加进去。
当 m≤n−1 时,我们需要最小化距离为 n 的点对数,所以肯定是连出一个大小为 m+1 的联通块,剩下的点都是孤立点。在这个联通块中,为了最小化内部的距离和,肯定是连成一个菊花的形状,即一个点和剩下所有点直接相邻。
当 m>n−1 时,肯定先用最开始 n−1 条边连成一个菊花,这时任意两点之间距离的最大值是 2。因此剩下的每一条边唯一的作用就是将一对点的距离缩减为 1。
这样我们就能知道了最终图的形状了,稍加计算就能得到答案。要注意 m 有可能大于 2n(n−1)。
1008 Rikka with Subset
发现每一个数都只能由比他更小的数组成,所以可以由B[0]直接求出0的个数,再求出1的个,2的个数。。。。在这个过程中,为了求出n的个数,只需求出0~n-1组成了多少个n即可。这就相当于一个背包问题,每次求出新数时当作新物品计算即可
1009 Rikka with Number
首先转化成计算小于等于 N 的好数有多少个。因为 nn<(n+1)n,而对于 n 进制下的任何一个好数 K,都有 nn−1<K<nn,所以每一个进制下好数的大小区间是不相交的。
不难发现 d 进制下好数的个数为 d!−(d−1)!,因此我们只需要计算在临界的 d 进制下,好数的个数就可以了。关于临界的 d,可以用对数估计位数得到。
求 d 进制下小于等于 N 的好数个数,先讲 N 转化成 d 进制,然后做一个类似康托展开的过程就可以了,因为数据范围很小,所以每一步操作都可以暴力进行。
时间复杂度 O(∣R∣2)。