cf vp
1112 div1
\(C\) 忘记了用连通块刻画连续段,没想到点边容斥。
\(D\) 的构造比较神秘。其中 \([0,L-1]\) 的异或稳定子构成的集合是 \([0,\lowbit(L)-1]\)。
\(F\) 中 \(i \in [0,2^x-1],popcount_k(i)\) 构成的字符串具有倍增的递推关系。
A. Rank Subsequence
给定长度为 \(n\) 的序列 \(a\)。其中第 \(i\) 个元素在子序列中时,满足子序列中在其之前的元素个数不属于 \([l_i,r_i]\),子序列中在其之后的元素个数不属于 \([u_i,v_i]\)。求最长子序列长度。
\(1 \le \sum n \le 5 \times 10^3,2s,256MB\)。
1
定义 \(f_{i,j}\) 表示考虑前 \(i\) 个元素子序列长度为 \(j\) 是否可行。
发现无法转移,因为不知道后面有多少元素。所以从大到小枚举子序列长度 \(len\),然后再 \(dp\)。
时间复杂度 \(O(n^3)\)。
2
套路地,考虑转 \(01\) 为最值。具体地,注意到 \(f_i\) 中有值部分是一个前缀,所以改变状态,定义 \(f_i\) 表示有值前缀的右端点。状态优化成 \(O(n)\)。
注意到只有 \(f_i\) 处的转移有效,所以转移也优化成 \(O(n)\)。
时间复杂度 \(O(n^2)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 5e3+10;
static int n,l[N],r[N],u[N],v[N];
inline bool check(int mid){
int now = 0;
FOR(i,1,n){
if(!(l[i]<=now+1&&now+1<=r[i])&&!(u[i]<=mid-now&&mid-now<=v[i]))++now;
}
return mid<=now;
}
inline void solve(){
//rd
readx(n);
FOR(i,1,n)readx(l[i]),readx(r[i]),readx(u[i]),readx(v[i]);
//get ans
REP(i,n,1)if(check(i))return output(i),putchar(10),void();
output(0),putchar(10);
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
B. Permutation Cuts
对一个 \(n\) 阶排列 \(p\),\(\forall i \in [1,n-1],a_i = \min(\max_{j \le i}\{p_j\},\max_{j \ge i+1}\{p_j\})\)。
现在给定 \(a\),求有多少种合法的 \(n\) 阶排列 \(p\)。
\(2 \le \sum n \le 10^6,4s,256MB\)。
1
记 \(pm_i = \max_{j \le i}\{p_j\},sm_i = \max_{j \ge i}\{p_j\}\)
考虑 \(a_i\) 对 \(p\) 的限制。有 \(sm_{i+1} \gt pm_i = a_i\) 或者 \(\pm_i \gt sm_{i+1} = a_i\)。
因此,\(a_i = n\) 一定是不合法的。剩下的 \(a_i\) 应当构成一个单峰,并且值相同的 \(a_i\) 构成一段连续段。
记 \(a_i\) 中的最大值占据的连续段为 \([L,R]\),则 \(n\) 只能放在 \(R+1\) 或者 \(L\)。
枚举这两种情况,然后从大到小枚举值计算其放置方案数之积即可。
精细实现可以做到 \(O(n)\)。代码是 \(O(n \log n)\) 的。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <queue>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 1e6+10,mo = 998244353;
static int n,a[N],l[N],r[N],v[N],tp;
static bool vis[N];
static int id[N];
inline int cal(int pos){
std::priority_queue<int> ql,qr;
ql.push(0),qr.push(-(n+1));
FOR(i,1,tp){
if(r[i]<pos)ql.push(r[i]);
else if(l[i]>=pos)qr.push(-(l[i]+1));
else return 0;
}
FOR(i,1,n)id[i] = -1;
FOR(i,1,tp)id[v[i]] = i;
int ans = 1;
REP(i,n-1,1){
int t;
if((t=id[i])==-1){
int l = ql.top(),r = -qr.top();
++l,--r;
ans = 1ll*ans*std::max(0,(r-l+1-(n-i)))%mo;
}
else{
if(r[t]<pos){
ql.pop();
int tl = ql.top();
ans = 1ll*ans*std::max(0,l[t]-tl)%mo;
}
else{
qr.pop();
int tr = -qr.top();
ans = 1ll*ans*std::max(0,tr-(r[t]+1))%mo;
}
}
if(!ans)return 0;
}
return ans;
}
inline void solve(){
readx(n);
FOR(i,1,n)vis[i] = 0;
tp = 0;
FOR(i,1,n-1)readx(a[i]);
a[n] = -1;
FOR(i,2,n)if(a[i-1]!=a[i]){
int tv = a[i-1];
if(vis[tv]||tv==n)return output(0),putchar(10),void();
vis[tv] = 1;
int j = i-1;
for(;a[j]==tv;--j);
l[++tp] = j+1,r[tp] = i-1;
v[tp] = tv;
}
v[tp+1] = v[0] = 0;
l[tp+1] = n;
r[0] = 0;
int mx = -1,id = -1;
FOR(i,1,tp)if(mx<v[i])mx = v[i],id = i;
if(id==-1)puts("id_err"),exit(0);
FOR(i,1,id-1)if(v[i]>v[i+1])return output(0),putchar(10),void();
FOR(i,id+1,tp)if(v[i]>v[i-1])return output(0),putchar(10),void();
int ans = 0;
ans = (ans+1ll*cal(l[id])*(l[id]-r[id-1]))%mo;
ans = (ans+1ll*cal(r[id]+1)*(l[id+1]-r[id]))%mo;
output(ans),putchar(10);
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
C. Double-Rift Dial
计算 \(n\) 阶排列 \(p\) 有多少个循环移位满足所有前缀都满足从小到大排序后只有至多两个连续段。
\(1 \le \sum n \le 2 \times 10^5,2s,256MB\)。
1
先断环成链,变成对区间计算答案。
当时就觉得这个至多 \(2\) 很有用,因此一直想着一些神秘的 adhoc 做法。
实际上不如强化限制,比如至多有 \(k\) 个连续段怎么做。
这样求我们在从右往左扫描左端点的过程中,实时维护每个右端点的连续段数。
考虑将连续段数刻画成连通块数,因此考虑点边容斥,用支持区间加+区间最小值的线段树维护连通块数即可。
时间复杂度 \(O(n \log n)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
const int N = 2e5+10;
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
struct D0{
#define ls (x<<1)
#define rs (x<<1|1)
#define mid ((l+r)>>1)
int mx[N<<3],ad[N<<3];
inline void pushup(int x){
mx[x] = std::max(mx[ls],mx[rs])+ad[x];
}
inline void add_ad(int x,int d){
mx[x]+=d,ad[x]+=d;
}
void bd(int x,int l,int r){
mx[x] = ad[x] = 0;
if(l==r)return;
bd(ls,l,mid),bd(rs,mid+1,r);
}
void my(int x,int l,int r,int pl,int pr,int d){
// if(x==1)printf("x:%d pl:%d pr:%d d:%d\n",x,pl,pr,d);
if(r<pl||l>pr)return;
if(pl<=l&&r<=pr)return add_ad(x,d),void();
my(ls,l,mid,pl,pr,d),my(rs,mid+1,r,pl,pr,d);
pushup(x);
}
int qy(int x,int l,int r,int pl,int pr){
if(r<pl||l>pr)return 0;
if(pl<=l&&r<=pr)return mx[x];
return std::max(qy(ls,l,mid,pl,pr),qy(rs,mid+1,r,pl,pr))+ad[x];
}
#undef ls
#undef rs
#undef mid
}t0;
static int a[N<<1],n;
static int la[N];
inline void solve(){
//rd
readx(n);
FOR(i,1,n)readx(a[i]),a[i+n] = a[i];
//solve
t0.bd(1,1,2*n);
FOR(i,0,n+1)la[i] = 2*n+1;
int ans = 0;
// FOR(i,1,2*n)printf("%d ",a[i]);putchar(10);
REP(i,2*n,1){
int p = la[a[i]-1];
if(p!=2*n+1)t0.my(1,1,2*n,p,2*n,-1);
p = la[a[i]+1];
if(p!=2*n+1)t0.my(1,1,2*n,p,2*n,-1);
t0.my(1,1,2*n,i,2*n,1);
la[a[i]] = i;
if(i<=n){
// printf("i:%d mx:%d\n",i,t0.qy(1,1,2*n,i,i+n-1));
if(t0.qy(1,1,2*n,i,i+n-1)<=2)++ans;
}
}
output(ans),putchar(10);
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
D. Xor Permutation Matrix
要求输出一个 \(n \times n\) 的矩形,满足每行每列构成一个 \(n\) 阶排列(\(0 index\)),且每个 \(2 \times 2\) 的子矩形的异或和均为 \(x\)。
\(1 \le \sum n \le 2500,2s,512MB\)。
1
先判断一下无解。
当 \(n=2\) 时,只有 \(x=0\) 有解。
接着手玩一下。感觉这个排列的限制是比较严的。为了在构造的过程中恰好出现一个排列,感觉上 \([0,n-1]\) 要具有较好的封闭性,结合样例猜一手当 \(n = 2^x\) 时有解。
考虑浅证一下。不难发现对于相邻两行 \(i,i+1\),如果定义 \(d_j = a_{i,j} \oplus a_{i+1,j}\),则奇数列的 \(d_j\) 和偶数列的 \(d_j\) 均相同,并且二者异或和恰为 \(x\)。
考虑抵消掉 \(d_{j+1} = d_j \oplus x\)。不妨考察 \(i,i+2\) 两行,定义 \(d_j = a_{i,j} \oplus a_{i+2,j}\)。发现所有 \(d_j\) 均相等。
进一步的推广,可以得到与 \(i\) 奇偶性相同的行都具有如上性质。
相当于记集合 \(S = \{x|0 \le x \le n-1,x \in \Z\}\)。要求 \(T = \{x|x = t \oplus d_i,t \in S\}\) 恰好等于 \(S\)。并且可以找到 \(\lfloor \dfrac{n}{2} \rfloor\) 个互不相同的 \(d_i\)(因为要求列也构成排列)。
这时有结论:对集合 \(S = \{x|0 \le x \le L-1\}\) ,其所有异或稳定子构成的集合 \(T = \{x|0 \le x \le \lowbit(L)-1\}\)。可以感性理解一下。
因此 \(n = 2^k\) 是有解的必要条件。并且在此基础上易得 \(x \le n-1\) 也是有解的必要条件。
2
猜测剩下的情况都是有解的。考虑构造性证明。
当 \(x=0\) 时,注意到此时所有可用数都是异或稳定子,所以不妨大胆定义 \(a_{i,j} = i \oplus j\)。这样一行相当于是一个 \([1 \cdots n]\) 的排列异或上 \(i\),仍然是排列。容易验证其满足题目所有要求。
剩下情况,考虑大胆引入 \(x\)。比如不妨定义 \(a_{i,j} = i \oplus j \oplus ((i \bmod 2)\cdot(i \bmod 2)\cdot x)\)。容易验证这可以满足子矩形的异或要求,但不能满足排列的要求,原因是引入了 \(x\)。
注意到我们令 \(a_{i,j} = p_i \oplus p_j \oplus ((i \bmod 2)\cdot(i \bmod 2)\cdot x)\),其中 \(p\) 是排列,本质相同。
所以我们现在希望 \(p\) 的所有奇数位构成的集合的一个异或稳定子是 \(x\)。而注意到 \(x\) 是原集合的异或稳定子,所有我们可以将所有 \(y,y \oplus x\) 两两配对。然后取出一半的对放在奇数位,剩下放在偶数位就做完了。
时间复杂度 \(O(n^2)\)。
代码
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
static char stkk[200];
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 1e5+10;
static int n,x,p[N];
static bool vis[N];
struct node{
int x,y;
}stk[N];
inline void solve(){
readx(n),readx(x);
if(n!=(1<<(int)std::__lg(n))||(n==2&&x!=0)||(x>=n))return output(-1),putchar(10),void();
if(x==0){
FOR(i,0,n-1){FOR(j,0,n-1)output(i^j),putchar(' ');putchar(10);}
return;
}
FOR(i,0,n-1)vis[i] = 0;
int tp = 0;
FOR(i,0,n-1){
if(!vis[i]){
vis[i] = vis[i^x] = 1;
stk[++tp] = (node){i,i^x};
}
}
int ttp = 0;
FOR(i,1,tp/2)p[++ttp] = stk[i].x,++ttp,p[++ttp] = stk[i].y,++ttp;
ttp = 1;
FOR(i,tp/2+1,tp)p[++ttp] = stk[i].x,++ttp,p[++ttp] = stk[i].y,++ttp;
FOR(i,0,n-1){FOR(j,0,n-1)output(p[i+1]^p[j+1]^(((i&1)&&(j&1))*x)),putchar(' ');putchar(10);}
}
signed main(){
int T;for(readx(T);T--;solve());
return 0;
}
E1. String (Easy Version)
对一个 \(k\) 进制数 \(x\) 定义 \(p_k(x)\) 表示各位数之和膜 \(k\) 的值。
\(q\) 次询问 \(\forall i \in [l,r]\),\(p_k(i)\) 顺次拼接构成的文本串 \(S\) 中,模式串 \(T\) 的出现次数。
\(2 \le k \le 10,1 \le q \le 10^3,1 \le \sum |T| \le 2 \times 10^6,1 \le l \le r \le 10^{17}\)。
1
定义 \(ts_{j,i}\) 表示 \([0,k^i-1]\) 内的数顺次拼接,并且整体加 \(j\) 后构成的字符串。
首先可以将 \([l,r]\) 拆成 \(O(k \log_{k}{V})\) 个 \(ts_{j,i}\)。
考虑如何合并这些 \(ts_{j,i}\),发现我们只关心内部 \(T\) 出现次数,至多长 \(n-1\) 的前/后缀,合并直接暴力跑 \(kmp\) 即可。
考虑如何递推 \(ts\),其中 \(ts_{j,0}\) 是好做的。注意到 \(ts_{j,i}\) 等于 \(ts_{j,i-1}\) 依次平移 \(0,1,\cdots,k-1\) 后的拼接的结果。
时间复杂度 \(O(k^2\log_{k}{V}\sum |T|)\)。无法通过。
2
记 \(mx\) 表示第一个 \(k^j \ge n-1\) 的 \(j\)。注意到 \(\forall i \ge mx\),\(ts_{j,i}\) 的前后缀都是某个 \(ts_{j',mx}\),而只有 \(k^2\) 个本质不同的合并结果,可以预处理。
如果预分配内存存储前后缀,对 \(i \ge mx\) \(O(1)\) 赋值的话,预处理的复杂度变成 \(O(k^2\sum |T|)\)。
而询问直接暴力合并,复杂度 \(O(k\log_{k}{V}\sum |T|)\)。
总时间复杂度 \(O(k(\log_{k}{V}+k)\sum |T|)\)。
代码由 gemini 3.1 pro 卡常
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <cstring>
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define ll long long
// --- 极致 Fast IO ---
namespace IO {
const int BUF_SIZE = 1 << 21;
char buf[BUF_SIZE], *p1 = buf, *p2 = buf;
inline char gc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, BUF_SIZE, stdin), p1 == p2) ? EOF : *p1++;
}
char out_buf[BUF_SIZE], *out_p = out_buf;
inline void flush() {
fwrite(out_buf, 1, out_p - out_buf, stdout);
out_p = out_buf;
}
inline void pc(char c) {
if (out_p - out_buf == BUF_SIZE) flush();
*out_p++ = c;
}
template<typename T> inline void readx(T &x) {
x = 0; int y = 1; char c = gc();
for (; c < 48 || c > 58; c = gc()) if (c == '-') y = -1;
for (; c >= 48 && c <= 58; c = gc()) x = (x << 1) + (x << 3) + (c ^ 48);
x *= y;
}
static char stkk[200];
template<typename T> inline void output(T x) {
if (!x) return pc('0'), void();
if (x < 0) x = ~x + 1, pc('-');
int top = 0;
for (; x; stkk[++top] = x % 10 ^ 48, x /= 10);
for (; top; pc(stkk[top--]));
}
struct IO_Flusher { ~IO_Flusher() { flush(); } } flusher;
}
using IO::readx;
using IO::output;
using IO::pc;
const int N = 2e6+10, K = 10, M = 64;
static int k, q, n;
static char s[N];
static int kmp[N];
static int f[K][K];
// --- 内存池 ---
char pool[500000000]; // 150MB 内存池,足够单次询问使用
char* ptop = pool;
struct MyVec {
char* ch;
int len, j;
void clear() {
ch = nullptr;
len = j = 0;
}
};
inline int cal(const MyVec &s_vec, const MyVec &p_vec) {
if (s_vec.len + p_vec.len < n || n == 1) return 0;
int j = 0, ans = 0;
REP(i, s_vec.len - 1, 0) {
char c = s_vec.ch[i];
while (j && s[j + 1] != c) j = kmp[j];
if (s[j + 1] == c) ++j;
if (j == n) ++ans, j = kmp[j];
}
FOR(i, 0, p_vec.len - 1) {
char c = p_vec.ch[i];
while (j && s[j + 1] != c) j = kmp[j];
if (s[j + 1] == c) ++j;
if (j == n) ++ans, j = kmp[j];
}
return ans;
}
inline void adx(int &x, int y) { if ((x += y) >= k) x -= k; }
struct node {
MyVec p, s_nd;
bool t;
ll su;
void init() {
p.clear(); s_nd.clear();
t = su = 0;
}
int get(const node& A) const {
return cal(s_nd, A.p);
}
node& operator+=(const node& A) {
if (t && A.t) {
su += A.su + f[s_nd.j][A.p.j];
s_nd = A.s_nd; // O(1) 浅拷贝
} else {
su += A.su + get(A);
// 处理前缀 p
int add_p = std::min(A.p.len, n - 1 - p.len);
if (add_p > 0) {
char* nch = ptop; ptop += p.len + add_p;
if (p.len > 0) memcpy(nch, p.ch, p.len);
memcpy(nch + p.len, A.p.ch, add_p);
p.ch = nch;
p.len += add_p;
}
// 处理后缀 s_nd
int add_s = std::min(s_nd.len, n - 1 - A.s_nd.len);
if (add_s > 0) {
char* nch = ptop; ptop += A.s_nd.len + add_s;
if (A.s_nd.len > 0) memcpy(nch, A.s_nd.ch, A.s_nd.len);
memcpy(nch + A.s_nd.len, s_nd.ch, add_s);
s_nd.ch = nch;
s_nd.len = A.s_nd.len + add_s;
} else {
s_nd.ch = A.s_nd.ch; // O(1) 浅拷贝
s_nd.len = A.s_nd.len;
}
s_nd.j = A.s_nd.j;
t = A.t;
}
return *this;
}
node operator+(const node& A) const {
node c = *this;
return (c += A);
}
};
static node ts[K][M], stk[M << 2];
static int tp;
static ll pw[M];
static int tl[M], tpl, tr[M], tpr;
static int gs[M];
signed main() {
readx(k), readx(q);
pw[0] = 1;
int gmx = 0;
FOR(i, 1, M - 1) {
if ((pw[i] = 1ll * pw[i - 1] * k) >= 1e17) { gmx = i; break; }
}
ll l, r;
FOR(query_idx, 1, q) {
ptop = pool; // 每次询问清空内存池,O(1) 回收所有内存
readx(l), readx(r), readx(n);
char c = IO::gc();
while(c <= 32) c = IO::gc();
FOR(idx, 1, n) {
s[idx] = c;
c = IO::gc();
}
FOR(j, 0, n + 1) kmp[j] = 0;
FOR(j, 1, n) s[j] ^= 48;
s[n + 1] = -1;
int j = 0;
FOR(idx, 2, n) {
for (; j && s[j + 1] != s[idx]; j = kmp[j]);
if (s[j + 1] == s[idx]) ++j;
kmp[idx] = j;
}
if (l > r) { output(0), pc(10); continue; }
if (l == r) {
if (n == 1) {
int now = 0;
for (; l; adx(now, l % k), l /= k);
output(now == s[1]), pc(10);
} else output(0), pc(10);
continue;
}
tpl = 0; for (; l; tl[++tpl] = l % k, l /= k);
tpr = 0; for (; r; tr[++tpr] = r % k, r /= k);
FOR(idx, tpl + 1, tpr) tl[idx] = 0;
int mx = -1;
FOR(idx, 0, gmx) if (pw[idx] >= n - 1) { mx = idx; break; }
if (mx == -1) exit(0);
FOR(j, 0, k - 1) FOR(idx, 0, M - 1) ts[j][idx].init();
FOR(j, 0, k - 1) {
node &nd = ts[j][0];
if (n == 1) nd.su = j == s[1];
nd.p.ch = ptop; *ptop++ = j; nd.p.len = 1;
nd.s_nd.ch = ptop; *ptop++ = j; nd.s_nd.len = 1;
}
FOR(idx, 1, mx) {
FOR(j, 0, k - 1) {
int tx = j;
node &nd = ts[j][idx];
nd = ts[tx][idx - 1]; // O(1) 浅拷贝
FOR(step, 1, k - 1) {
adx(tx, 1);
nd += ts[tx][idx - 1];
}
}
}
FOR(idx, 0, k - 1) FOR(j, 0, k - 1) f[idx][j] = ts[idx][mx].get(ts[j][mx]);
FOR(j, 0, k - 1) {
node &nd = ts[j][mx];
nd.t = 1;
nd.p.j = nd.s_nd.j = j;
}
FOR(idx, mx + 1, gmx) {
FOR(j, 0, k - 1) {
int tx = j;
node &nd = ts[j][idx];
nd = ts[tx][idx - 1]; // O(1) 浅拷贝
FOR(step, 1, k - 1) {
adx(tx, 1);
nd += ts[tx][idx - 1];
}
}
}
int id = -1;
REP(idx, tpr, 1) if (tl[idx] != tr[idx]) { id = idx; break; }
if (id == -1) exit(0);
tp = 0;
gs[tpr + 1] = 0;
REP(idx, tpr, 1) adx(gs[idx] = gs[idx + 1], tl[idx]);
stk[++tp] = ts[gs[1]][0];
FOR(idx, 1, id - 1) {
FOR(j, tl[idx] + 1, k - 1) {
int tx; adx(tx = j, gs[idx + 1]);
stk[++tp] = ts[tx][idx - 1];
}
}
FOR(j, tl[id] + 1, tr[id] - 1) {
int tx; adx(tx = j, gs[id + 1]);
stk[++tp] = ts[tx][id - 1];
}
REP(idx, tpr, 1) adx(gs[idx] = gs[idx + 1], tr[idx]);
REP(idx, id - 1, 1) {
FOR(j, 0, tr[idx] - 1) {
int tx; adx(tx = j, gs[idx + 1]);
stk[++tp] = ts[tx][idx - 1];
}
}
stk[++tp] = ts[gs[1]][0];
FOR(idx, 1, tp) stk[idx].t = 0;
FOR(idx, 2, tp) stk[1] += stk[idx];
output(stk[1].su), pc(10);
}
return 0;
}
E2. String (Hard Version)
\(2 \le k \le 62,1 \le q \le 10^4,1 \le |T| \le 2 \times 10^6,1 \le l \le r \le 10^{17}\)。
1
注意到 \(ts_{j,i}\) 的基本储存单位是 \(0,1,\cdots,k-1\) 整体平移 \(j\) 后组成的结果,所以不妨用 \(t_j\) 来表示这么一块。
现在考虑匹配。注意到同一块内的差分数组均为 \(1\)。不妨维护 \(T\) 的差分数组,然后找到其中非 \(1\) 的位置。这些位置的间距理应是 \(k\) 的倍数,然后将 \(T\) 缩成 \(t\)。这样整体复杂度可以除一个 \(k\),为 \(O((k+\log_{k}{V})\sum |T|)\)。
那如果没有非 \(1\) 位置呢?感性理解这样的串如果太长,必然找不到匹配;短的时候暴力跑原本的算法即可。
不存在代码实现。

浙公网安备 33010602011771号