E
m次操作,每次加边后询问最大旅行团。
旅行团的定义:旅行团中的每个人都至少有k个邻接点在团里。
F
function z(array a, integer k):
if length(a) < k:
return 0
else:
b = empty array
ans = 0
for i = 0 .. (length(a) - k):
temp = a[i]
for j = i .. (i + k - 1):
temp = max(temp, a[j])
append temp to the end of b
ans = ans + temp
return ans + z(b, k)
给出 \(a,k\),求 \(z(a,k)\)
考虑k=3的情况。
数组变化如下
\[[1,3],[2,4],[3,5]...\\ [1,5],[2,6],[3,7]...\\ [1,7]...\\ ...\\ [1,gap+1]...[n-gap,n] \]也就是说 \(gap=r-l\) 的值是 \((k-1),2(k-1),...\)
枚举gap来计算是困难的,考虑 \(a_i\) 的贡献,\(a_i\) 向左连续不大于他的最左的是 \(a_l\),向右连续小于他的最右的是 \(a_r\)(为了防止相同的 \(a_i\) 干扰计算)
设 \(calc(l,r)\) 为区间 \(l,r\) 中会出现的区间个数,那么 \(a_i\) 贡献次数为 \(calc_{l,r}-calc_{l,i-1}-calc_{i+1,r}\),这个容易计算
G
vp写完F还有5min,然后pbb吹水去了。。看了半天题解。。
A和B玩游戏,二人轮流操作,A先手。
初始有一个字符串 \(s\),每次操作玩家可以选一个喜欢的字符 \(c\),把字符串 \(c\) 删掉,这样字符串就断开成多个,也就变成多个子游戏。不能操作者输。
设字符 \(c\) 在字符串中出现位置为 \(a_1,a_2,...,a_k\)
会把一个字符串分割成上图那样。(绿色、红色部分内部没有字符 \(c\))
首先,子游戏SG值异或和是当前游戏的SG值(所以对于下图那个字符串的进行操作 \(c\) 后该状态的SG值就是绿色部分的异或和)
如果每次遇到询问都暴力求解SG函数值显然太慢了,我们发现有大量的重复计算部分。
那么可以dp预处理绿色部分(指对于每个 \(c\),每个绿色部分单独当成一个询问那样预处理),红色部分的数量是 \(O(26n)\) 级别的,可以记忆化。
对于每个字符 \(c\),对绿色部分做前缀和。然后就可以在 \(O(26n)\) 的复杂度内计算的所有询问的SG函数值。
具体实现可以看下代码,各数组的定义应该很容易得出(
那个SG值求mex的地方看着比较怪,官方题解是这么写的(暴露了我的补题写的和官方std差不多的事实),原因是你顶多 26 个后继,mex 取值肯定在 \([0,26]\) 内。
const int N = 1e5 + 10, alf = 26;
char s[N]; int n, tmp[30], lst[30][N], nxt[30][N], dplst[30][N], dpnxt[30][N], dp[30][N], rk[N];
vector<int>vec[30];
vector<PII>seg;
bool cmp(PII x, PII y) { return x.se - x.fi < y.se - y.fi; }
int dp_slow(int l, int r) {
int mask = 0;
for(int c = 0; c < alf; c++) {
int L = nxt[c][l], R = lst[c][r];
if(L > r) continue;
int ret = 0;
for(int i = rk[L] + 1; i <= rk[R]; i++) ret ^= dp[c][i];
if(l < L) {
if(dpnxt[c][l] == -1) dpnxt[c][l] = dp_slow(l, L - 1);
ret ^= dpnxt[c][l];
}
if(R < r) {
if(dplst[c][r] == -1) dplst[c][r] = dp_slow(R + 1, r);
ret ^= dplst[c][r];
}
if(ret < alf) mask |= (1 << ret);
}
for(int i = 0; i < alf; i++)
if(!(mask & (1 << i))) return i;
return alf;
}
int dp_fast(int l, int r) {
int mask = 0;
for(int c = 0; c < alf; c++) {
int L = nxt[c][l], R = lst[c][r];
if(L > r) continue;
//cout<<"dp_fastw: "<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
int ret = (dp[c][rk[R]] ^ dp[c][rk[L]]);
if(l < L) {
if(dpnxt[c][l] == -1) dpnxt[c][l] = dp_fast(l, L - 1);
ret ^= dpnxt[c][l];
}
if(R < r) {
if(dplst[c][r] == -1) dplst[c][r] = dp_fast(R + 1, r);
ret ^= dplst[c][r];
}
if(ret < alf) mask |= (1 << ret);
}
for(int i = 0; i < alf; i++)
if(!(mask & (1 << i))) return i;
return alf;
}
void init() {
for(int i = 1; i <= n; i++) {
int c = s[i] - 'a';
if(vec[c].size()) seg.push_back(mkp(vec[c].back(), i));
vec[c].push_back(i); rk[i] = vec[c].size() - 1;
}
for(int i = 0; i < alf; i++) tmp[i] = 0;
for(int i = 1; i <= n; i++) {
int c = s[i] - 'a'; tmp[c] = i;
for(int j = 0; j < alf; j++)
lst[j][i] = tmp[j];
}
for(int i = 0; i < alf; i++) tmp[i] = n + 1;
for(int i = n; i >= 1; i--) {
int c = s[i] - 'a'; tmp[c] = i;
for(int j = 0; j < alf; j++)
nxt[j][i] = tmp[j];
}
memset(dplst, -1, sizeof(dplst));
memset(dpnxt, -1, sizeof(dpnxt));
sort(seg.begin(), seg.end(), cmp);
for(int i = 0; i < seg.size(); i++) {
int l = seg[i].fi, r = seg[i].se;
if(r - l == 1) continue;
dp[s[r] - 'a'][rk[r]] = dp_slow(l + 1, r - 1);
}
// cout<<"??";
for(int c = 0; c < alf; c++)
for(int i = 1; i < vec[c].size(); i++)
dp[c][i] ^= dp[c][i - 1];
return;
}
int main() {
scanf("%s", s + 1); n = strlen(s + 1);
init();
int m; scanf("%d", &m);
while(m--) {
int l, r; scanf("%d%d", &l, &r);
if(dp_fast(l, r) == 0) puts("Bob");
else puts("Alice");
}
return 0;
}