[八省联考 2018] 制胡窜 题解

[八省联考 2018] 制胡窜

比较套路纸糊船题。

看到这种判断一个串是否出现在一个区间里的,直接想 SA 或者 SAM。但萌新不会 SAM,只能用 SA 硬上。

起手容斥,对于一个询问 [l,r],记性质 A 表示其在 [1,i] 中出现过,同理 B 表示其在 [i+1,j1] 中出现过,C 表示其在 [j,n] 中出现过,记 P(M) 为满足 M 性质区间 [i,j] 的个数。显然有

Ans=P(ABC)=P(A)+P(B)+P(C)P(AB)P(AC)P(BC)+P(ABC)

发现只有带 B 的难求,先不考虑这个,考虑如何求只有 A,C 的。

应用 品酒大会 那道题的套路,将询问离线,倒序枚举 len,对于所有的 htilen 的,将 saisai1 连边,用并查集维护,那么现在所有与 S[l,r] 相等的子串的起始位置都与 l 在同一连通块中。

s 表示 l 所在的连通块中的最小值,t 表示 l 所在的连通块中的最大值,len=rl+1。那么就有 P(A)=i=s+len1n2j=i+2n1,同理 P(B)=i=1t2j=i+2t1P(C)=i=s+len1t2j=i+2t1。发现这些东西都是同构的,都是形如 i=st2j=i+2t,那么记其为 f(s,t),直接等差数列就有 f(s,t)=(ts)(ts1)/2,注意如果当 s+2>t 时值为 0

不包含 B 的考虑完了,考虑包含 B 的怎么做。

g(a,b) 表示 ai&jb,满足 B 性质的合法 [i,j] 数,显然 P(B)=g(1,n)P(AB)=g(s+len1,n)P(BC)=g(1,t)P(ABC)=g(s+len1,t)。考虑如何求 g

假设现在要求 g(a,b)。由于一个区间 [i,j] 满足性质 B 当且仅当 l 所在的连通块中有一个数 x[i+1,j1]x+len1b,那么就求 [a+1,blen] 中与 l 在同一个连通块中的位置,考虑其贡献。

假设总共有 K 个位置满足上述性质,且第 i 个位置为 qi,钦定 q0=a,考虑枚举合法区间的第一个与 [l,r] 相同的位置。显然有h(α)=i=qα1qα1j=qα+lenb1=(bqαlen+1)(qαqβ)=(blen+1)(qαqβ)qα2+qαqβ,其中β=α1,然后记其为 h(α)。那么 g(a,b)=α=1Kh(α)=(blen+1)(qKa)+a×q1α=1Kqα2+α=2Kqαqα1。第一项和第二项直接求最大值计算,后面两项用线段树维护即可。对于合并两个连通块,直接上线段树合并即可。

没卡常,目前你谷第 3 优解。代码有点细节。

code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
#define ep emplace
const int N = 1e5 + 10;
vector<tuple<int,int,int> > Q[N];
int n,m;ll ans[N*3];
char s[N];int rk[N],ct[N],ht[N],sa[N],tp[N];
void Get_SA(){
int m = 10;
auto _G = [](char x){return x - '0' + 1;};
auto Qsort = [&](){
rep(i,1,m,1) ct[i] = 0;
rep(i,1,n,1) ct[rk[i]]++;
rep(i,1,m,1) ct[i] += ct[i-1];
drep(i,n,1,1) sa[ct[rk[tp[i]]]--] = tp[i];
};
rep(i,1,n,1) rk[i] = _G(s[i]),tp[i] = i;
Qsort();
for(int w = 1,p = 0;w <= n;w <<= 1,m = p,p = 0){
rep(i,n-w+1,n,1) tp[++p] = i;
rep(i,1,n,1) if(sa[i] > w) tp[++p] = sa[i] - w;
Qsort();rep(i,1,n,1) tp[i] = rk[i];
p = rk[sa[1]] = 1;
auto cmp = [&](int x,int y){return tp[x] == tp[y] && tp[x + w] == tp[y + w];};
rep(i,2,n,1) rk[sa[i]] = cmp(sa[i-1],sa[i])?p:++p;
if(p == n) break;
}
for(int i = 1,k = 0;i <= n; ++i){
if(k) --k;
int j = sa[rk[i]-1];
while(s[i + k] == s[j + k]) k++;
ht[rk[i]] = k;
}
}
int fa[N],rt[N];
int GF(int x){while(x ^ fa[x]) x = fa[x] = fa[fa[x]];return x;}
struct Segment_Tree{
struct node{
int ls,rs,mn,mx;
ll sum,val;
#define ls(x) t[x].ls
#define rs(x) t[x].rs
#define mn(x) t[x].mn
#define mx(x) t[x].mx
#define sum(x) t[x].sum
#define val(x) t[x].val
}t[N*30];
using tpl = tuple<int,int,ll,ll>;
#define mt make_tuple
int tot;
Segment_Tree(){t[0].mn = 0;tot = 0;}
void P(int p,int q){val(p) = val(q);sum(p) = sum(q);mn(p) = mn(q),mx(p) = mx(q);}
void U(int p){
if(!ls(p) && !rs(p)) return;
if(!ls(p)) return P(p,rs(p));
if(!rs(p)) return P(p,ls(p));
val(p) = val(ls(p)) + val(rs(p)) + 1ll*mx(ls(p))*mn(rs(p));
sum(p) = sum(ls(p)) + sum(rs(p));
mn(p) = min(mn(ls(p)),mn(rs(p)));
mx(p) = max(mx(ls(p)),mx(rs(p)));
}
void C(int &now,int p,int l = 1,int r = n){
if(!now) now = ++tot;
if(l == r) return val(now) = 0,mn(now) = mx(now) = p,sum(now) = 1ll*p*p,void();
int mid = (l + r) >> 1;
p <= mid?C(ls(now),p,l,mid):C(rs(now),p,mid+1,r);
U(now);
}
#define A(i,x) (get<i>(x))
tpl U(const auto &x,const auto &y){
return mt(min(A(0,x),A(0,y)),max(A(1,x),A(1,y)),A(2,x)+A(2,y),A(3,x)+A(3,y)+(A(0,y)==1e9?0ll:1ll*A(1,x)*A(0,y)));
}
tpl Q(int now,int ql,int qr,int l = 1,int r = n){
if(!now) return mt(1e9,0,0,0);
if(ql <= l && r <= qr) return mt(mn(now),mx(now),sum(now),val(now));
int mid = (l + r) >> 1;
if(ql > mid) return Q(rs(now),ql,qr,mid+1,r);
else if(qr <= mid) return Q(ls(now),ql,qr,l,mid);
else return U(Q(ls(now),ql,qr,l,mid),Q(rs(now),ql,qr,mid+1,r));
}
void M(int &x,const int &y,int l = 1,int r = n){
if(!x) return x = y,void();
if(!y) return;
int mid = (l + r) >> 1;
M(ls(x),ls(y),l,mid);M(rs(x),rs(y),mid+1,r);
U(x);
}
}sgt;
void Merge(int x,int y){
if(!x || !y) return;
x = GF(x),y = GF(y);
if(x == y) return;
fa[x] = y;sgt.M(rt[y],rt[x]);
}
void Work(){
auto f = [](int n,int m){return n + 2 > m?0ll:1ll*(m-n)*(m-n-1)/2;};
auto g = [&](int a,int b,int len,int x){
if(a + 1 > b-len) return 0ll;
ll res = 0;
auto [q1,qk,sum,val] = sgt.Q(rt[x],a+1,b-len);
if(!qk) return 0ll;
res += 1ll*(qk-a)*(b-len+1) + 1ll*a*q1;
res -= sum-val;
return res;
};
vector<pair<int,int> > vec;
rep(i,1,n,1) vec.emplace_back(ht[i],i);
sort(vec.begin(),vec.end(),greater<>());
int now = -1,siz = (int) vec.size() - 1;
drep(len,n,1,1){
if(Q[len].empty()) continue;
while(now < siz && vec[now + 1].first >= len) now++,Merge(sa[vec[now].second],sa[vec[now].second-1]);
for(const auto &[l,r,id]:Q[len]){
ll res = 0;int x = GF(l);
int s = sgt.t[rt[x]].mn,t = sgt.t[rt[x]].mx;
res += f(s+len-1,n);res += f(1,t);res -= f(s+len-1,t);
res += g(1,n,len,x);res -= g(s+len-1,n,len,x);
res += g(s+len-1,t,len,x);res -= g(1,t,len,x);
ans[id] = res;
}
}
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cin>>n>>m>>(s + 1);
rep(i,1,n,1) fa[i] = i,sgt.C(rt[i],i);
for(int i = 1,l,r;i <= m; ++i){
cin>>l>>r;
if(l == r) ans[i] = 1ll*(n-1)*(n-2)/2;
else if(r - l + 1 >= n - 1) ans[i] = 0;
else Q[r-l+1].emplace_back(l,r,i);
}
Get_SA();Work();
rep(i,1,m,1) cout<<ans[i]<<'\n';
}
posted @   CuFeO4  阅读(41)  评论(12编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示