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

[八省联考 2018] 制胡窜

比较套路纸糊船题。

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

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

\[Ans=P(A\cup B\cup C)=P(A)+P(B)+P(C)-P(AB)-P(AC)-P(BC)+P(ABC) \]

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

应用 品酒大会 那道题的套路,将询问离线,倒序枚举 \(len\),对于所有的 \(ht_i\ge len\) 的,将 \(sa_i\)\(sa_{i-1}\) 连边,用并查集维护,那么现在所有与 \(S_{[l,r]}\) 相等的子串的起始位置都与 \(l\) 在同一连通块中。

\(s\) 表示 \(l\) 所在的连通块中的最小值,\(t\) 表示 \(l\) 所在的连通块中的最大值,\(len=r-l+1\)。那么就有 \(P(A)=\sum\limits_{i=s+len-1}^{n-2}\sum\limits_{j=i+2}^n1\),同理 \(P(B)=\sum\limits_{i=1}^{t-2}\sum\limits_{j=i+2}^t1\)\(P(C)=\sum\limits_{i=s+len-1}^{t-2}\sum\limits_{j=i+2}^t1\)。发现这些东西都是同构的,都是形如 \(\sum\limits_{i=s}^{t-2}\sum\limits_{j=i+2}^{t}\),那么记其为 \(f(s,t)\),直接等差数列就有 \(f(s,t)=(t-s)(t-s-1)/2\),注意如果当 \(s+2>t\) 时值为 \(0\)

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

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

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

假设总共有 \(K\) 个位置满足上述性质,且第 \(i\) 个位置为 \(q_i\),钦定 \(q_0=a\),考虑枚举合法区间的第一个与 \([l,r]\) 相同的位置。显然有\(h(\alpha)=\sum\limits_{i=q_{\alpha-1}}^{q_{\alpha}-1}\sum\limits_{j=q_{\alpha}+len}^{b}1=(b-q_{\alpha}-len+1)(q_{\alpha}-q_{\beta})=(b-len+1)(q_{\alpha}-q_{\beta})-q_{\alpha}^2+q_{\alpha}q_{\beta}\),其中\(\beta=\alpha-1\),然后记其为 \(h(\alpha)\)。那么 \(g(a,b)=\sum\limits_{\alpha=1}^{K}h(\alpha)=(b-len+1)(q_K-a)+a\times q_1-\sum\limits_{\alpha=1}^Kq_{\alpha}^2+\sum\limits_{\alpha=2}^Kq_{\alpha}q_{\alpha-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 @ 2025-02-25 17:44  CuFeO4  阅读(60)  评论(12)    收藏  举报