A

考虑 dp。设 $f_{i,0/1}$ 表示考虑到根是 $i$ 前缀,最后一次删除的串长度为 $2/3$ 的方案数。

转移就判断一下两次长度相等是否子串相等即可。

B

这种最长的最短路链问题贪心超过两步都是错的。

首先 $O(n^2)$ 预处理 $dis_{i,j}$ 表示 $i \to j$ 的最短路,注意到贪心一步是正确的,我们去枚举 $b,c$,预处理最远,次远,第三远的点即可(因为路径不能重复)。

C

首先可以写出一个dp:设 $f_{i,j}$ 表示考虑填了前 $i$ 位,匹配了 $t$ 的前 $j$ 位的方案数。

转移:

$$ \begin{aligned} \text{if }j \neq m :\\ f_{i,j} \to 25f_{i+1,j}\\ f_{i,j} \to f_{i+1,j+1}\\ \text{if } j = m:\\ f_{i,j} \to 26f_{i+1,j} \end{aligned} $$

这种转移形式简单的计数问题可以抽象到网格图游走上:如果你当前在的坐标 $j \neq m$,可以选择走 $(1,0)$,方案 $25$,走 $(1,1)$ 方案数是 $1$;如果已经 $j=m$,那么只能走 $(1,0)$,方案数 $26$。

发现 $(1,1)$ 的数量恰为 $m$;考虑整个操作序列,在最后一个 $(1,1)$ 后的 $(1.0)$ 贡献都是 $26$,前面都是 $25$。考虑枚举最后一个 $(1,1)$:

$$ \sum_{i=m}^n \binom{i-1}{m-1}25^{i-m}26^{n-i} = \frac{26^n}{25^m}\sum_{i=0}^m \binom{i-1}{m-1} (\frac{25}{26})^i $$

注意 $\sum m \leq 10^5$,所以本质不同的 $m$ 只会有 $\sqrt m$ 个,每次都预处理所有答案,复杂度 $O(n \sqrt m)$。

D

这题暴力就行了。

我们先枚举每个点最终对应的点哪个坐标相等(可以看成是枚举在一条横线还是纵线上),然后分类讨论:

首先如果存在交点一定选交点要不然点就不够用了。

我们将所有的线去重之后分类讨论:

两横两纵

每个点都是确定的,判断是否是正方形后 $4!$ 枚举配对方案求答案即可。

两横一纵(或两纵一横)

我们确定了两个点和这个正方形的边长 $D$,所以另外两个点只有可能是坐标某一维 $\pm D$,对每一种情况 $4!$ 统计答案即可。

两横或两纵

我们考虑两横:

首先确定了正方形的边长 $D$,一个暴力的想法枚举一个点,就能确定所有的点。

假设现在上面的点是 $x_1,x_2$,下面的点是 $x_3,x_4$,不失一般性设当前枚举的两条竖线是 $x,x+D$。

不失一般性我们设 $x_1,x_3$ 匹配 $x$,$x_2,x_4$ 匹配 $x+D$(这里可以通过枚举完成所有情况),那么答案就是 $\max\{|x_1-x|,|x_3-x|,|x_2-(x+D)|,|x_4-(x+D)|\}$,拆开变成四个坐标 $x_1,x_3,x_2-D,x_4-D$,只需要取最大值和最小值的中点左右挨个判断一下即可。

代码太长了(170+),这里给出提交链接

E

先对所有的 $t$ 建出广义SAM,现在先考虑如何确定 $S[l,r]$ 对应的位置:我们先找到 $S[1,r]$ 对应的位置,不断跳 fail 并保证一直包含这个区间,需要用倍增预处理一下。

然后就直接线段树合并一发维护处最大值和最大值的位置就行了。

细节比较多,放一下代码:

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e5 + 5;

struct Node{
    int mx,ps;
    
    Node(int mx=0,int ps=-1) : mx(mx),ps(ps) {}
    
    friend Node operator + (const Node &x,const Node &y){
        Node res;
        if(x.mx >= y.mx) res.mx = x.mx,res.ps = x.ps;
        else res.mx = y.mx,res.ps = y.ps;
        return res;
    }
}sm[MAXN<<5];
// 最大值,非0位置个数
int lc[MAXN<<5],rc[MAXN<<5],tot;

inline Node query(int x,int l,int r,int L,int R){
    if(!x) return Node();
    if(l == L && r == R) return sm[x];
    int mid = (l + r) >> 1;
    if(R <= mid) return query(lc[x],l,mid,L,R);
    if(L > mid) return query(rc[x],mid+1,r,L,R);
    return query(lc[x],l,mid,L,mid)+query(rc[x],mid+1,r,mid+1,R);
}

inline void update(int &x,int l,int r,int p){
    if(!x) x = ++tot;
    if(l == r){
        sm[x].mx++;sm[x].ps = l;
        return;
    }
    int mid = (l + r) >> 1;
    if(p <= mid) update(lc[x],l,mid,p);
    else update(rc[x],mid+1,r,p);
    sm[x] = sm[lc[x]]+sm[rc[x]];
}

inline int merge(int x,int y,int l,int r){
    if(!x || !y) return x|y;
    int now = ++tot;
    if(l == r){
        sm[now] = sm[x];
        sm[now].mx += sm[y].mx;
        return now;
    }
    int mid = (l + r) >> 1;
    lc[now] = merge(lc[x],lc[y],l,mid);
    rc[now] = merge(rc[x],rc[y],mid+1,r);
    sm[now] = sm[lc[now]]+sm[rc[now]];
    return now;
}

int ch[MAXN][26],fail[MAXN],len[MAXN],_tot = 1,las = 1;
int rt[MAXN];

inline void copy(int x,int y){
    FOR(i,0,25) ch[x][i] = ch[y][i];
    fail[x] = fail[y];len[x] = len[y];
}

inline int work(int p,int c){
    int q = ch[p][c],nq = ++_tot;
    copy(nq,q);len[nq] = len[p]+1;fail[q] = nq;
    for(;p&&ch[p][c]==q;p=fail[p]) ch[p][c] = nq;
    return nq;
}

inline int insert(int p,int c){
    int q = ch[p][c];
    if(q){
        if(len[q] == len[p]+1) return q;
        return work(p,c);
    }
    int np = ++_tot;len[np] = len[p]+1;
    for(;p&&!ch[p][c];p=fail[p]) ch[p][c] = np;
    if(!p) fail[np] = 1;
    else{
        q = ch[p][c];
        if(len[q] == len[p]+1) fail[np] = q;
        else fail[np] = work(p,c);
    }
    return np;
}

const int MAXK = 5e5 + 5;

char S[MAXK],str[MAXN];
int n;
std::vector<int> G[MAXN];
const int MAXM = 16;
int f[MAXN][MAXM+1],m;

inline void dfs(int v){
    FOR(i,1,MAXM) f[v][i] = f[f[v][i-1]][i-1];
    for(auto x:G[v]){
        f[x][0] = v;dfs(x);
        rt[v] = merge(rt[v],rt[x],1,m);
    }
}

int to[MAXK],le[MAXK];

int main(){
    scanf("%s",S+1);n = strlen(S+1);
    scanf("%d",&m);
    FOR(i,1,m){
        las = 1;
        scanf("%s",str+1);int len = strlen(str+1);
        FOR(j,1,len) las = insert(las,str[j]-'a'),update(rt[las],1,m,i);
    }
    // DEBUG(query(rt[12],1,m,1,2).mx);
    // exit(0);
    FOR(i,2,_tot) G[fail[i]].pb(i);
    // for(auto x:G[81]){
        // DEBUG(x);
        // DEBUG(sm[rt[x]].mx);
        // for(auto y:G[x]){
            // DEBUG(y);
            // DEBUG(sm[rt[y]].mx);
        // }
    // }
    // exit(0);
    dfs(1);to[0] = 1;
    int now=1,len = 0;
    FOR(i,1,n){
        int c = S[i]-'a';
        if(ch[now][c]) len++,now = ch[now][c];
        else{
            while(now && !ch[now][c]) now = fail[now];
            if(!now) now = 1,len = 0;
            else len = ::len[now]+1,now = ch[now][c];
        }
        to[i] = now;le[i] = len;
    }
    int q;scanf("%d",&q);
    while(q--){
        int l,r,pl,pr;scanf("%d%d%d%d",&l,&r,&pl,&pr);
        int v = to[pr];
        if(le[pr] < pr-pl+1){
            printf("%d %d\n",l,0);
            continue;
        }
        ROF(i,MAXM,0){
            if(::len[f[v][i]] >= pr-pl+1){
                v = f[v][i];
            }
        }
        Node ans = query(rt[v],1,m,l,r);
        if(ans.ps == -1) printf("%d %d\n",l,0);
        else printf("%d %d\n",ans.ps,ans.mx);
    }
    return 0;
}
Last modification:September 15th, 2020 at 09:49 pm
如果觉得我的文章对你有用,请随意赞赏