P4248 [AHOI2013]差异(SAM/SA)

16 篇文章 0 订阅
题意:

给出一个长度为 n n n的字符串,求 ∑ 1 < = i < j < = n l e n ( T i ) + l e n ( T j ) − 2 ∗ l c p ( T i , T j ) \sum_{1<=i<j<=n} len(Ti)+len(Tj)−2∗lcp(Ti,Tj) 1<=i<j<=nlen(Ti)+len(Tj)2lcp(Ti,Tj)

题解:
方法一:

考虑把原串翻转过来,那么要求的就是所有前缀公共后缀了,两个前缀的最长公共后缀的答案就是这两个前缀所代表的点在后缀树上 L C A LCA LCA的长度,题目就转化为求树上某一个点是多少点对的 L C A LCA LCA再乘上 l e n [ L C A ] len[LCA] len[LCA]我们可以直接求出前半部分为: n ( n + 1 ) ( n − 1 ) 2 \frac{n(n+1)(n-1)}{2} 2n(n+1)(n1),然后我们对每个点考虑它作为 L C A LCA LCA的贡献值,再减去就好了。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e6+50;
char s[MAXN];
struct Suffix{
    int nxt[MAXN][26],fa[MAXN],len[MAXN],endpos[MAXN];
    int last=1,tot=1;
    vector<int> g[MAXN];
    LL res;
    inline void Insert(int x){
        int p = last, np = ++tot;
        last = np,len[np] = len[p] + 1;
        for(;p && !nxt[p][x];p = fa[p]) nxt[p][x] = np;
        if(!p) fa[np] = 1;
        else {
            int q = nxt[p][x];
            if(len[p]+1 == len[q]) fa[np] = q;
            else {
                int nq = ++tot;
                len[nq] = len[p] + 1;
                memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
                fa[nq] = fa[q];
                fa[q] = fa[np] = nq;
                for(;nxt[p][x]==q;p = fa[p])
                    nxt[p][x] = nq;
            }
        }
        endpos[np] = 1;
    }
    inline void dfs(int u){
        for(int i=0;i<(int)g[u].size();i++){
            int v = g[u][i];
            dfs(v);
            res -= 2LL*endpos[u]*endpos[v]*len[u];
            endpos[u] += endpos[v];
        }
    }
    inline void Solve(int n){
        for(int i=2;i<=tot;i++) g[fa[i]].push_back(i);
        res = 1LL*(n+1)*(n-1)*n/2;
        dfs(1);
        printf("%lld\n",res);
    }
}SAM;
int main(){
    scanf("%s",s+1);
    int len = strlen(s+1);
    for(int i=len;i>=1;i--) SAM.Insert(s[i]-'a');
    SAM.Solve(len);
    return 0;
}
方法二:

也是构造出反串的 p a r e n t parent parent树,然后根据父子关系dp出每个后缀的累加次数即可

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e6+50;
char s[MAXN];
struct Suffix{
    int nxt[MAXN][26],fa[MAXN],len[MAXN],endpos[MAXN];
    int c[MAXN],a[MAXN];
    LL dp[MAXN];
    int last=1,tot=1;
    vector<int> g[MAXN];
    LL res;
    inline void Insert(int x){
        int p = last, np = ++tot;
        last = np,len[np] = len[p] + 1;
        for(;p && !nxt[p][x];p = fa[p]) nxt[p][x] = np;
        if(!p) fa[np] = 1;
        else {
            int q = nxt[p][x];
            if(len[p]+1 == len[q]) fa[np] = q;
            else {
                int nq = ++tot;
                len[nq] = len[p] + 1;
                memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
                fa[nq] = fa[q];
                fa[q] = fa[np] = nq;
                for(;nxt[p][x]==q;p = fa[p])
                    nxt[p][x] = nq;
            }
        }
        endpos[np] = 1;
    }
    inline void Solve(int n){
        for(int i=1;i<=tot;i++) c[len[i]]++;
        for(int i=1;i<=tot;i++) c[i]+=c[i-1];
        for(int i=1;i<=tot;i++) a[c[len[i]]--]=i;
        for(int i=1;i<=tot;i++) dp[i]=endpos[i];
        for(int i=tot;i;i--) dp[fa[a[i]]] += dp[a[i]];
        LL res = 1LL*(n-1)*(n+1)*n/2;
        for(int i=2;i<=tot;i++)
            res -= 1LL*(dp[i]-1)*dp[i]*(len[i]-len[fa[i]]);
        printf("%lld\n",res);
    }
}SAM;
int main(){
    scanf("%s",s+1);
    int len = strlen(s+1);
    for(int i=len;i>=1;i--) SAM.Insert(s[i]-'a');
    SAM.Solve(len);
    return 0;
}
方法三:

后缀数组+单调栈

和上面一样,我们只需要求出每个 l c p lcp lcp即可,不难发现,对于排名为 i i i j j j的两个后缀,它们的 l c p lcp lcp应该是 m i n ( h [ i ] , h [ i + 1 ] , h [ i + 2 ] . . . h [ j ] ) min(h[i] ,h[i+1],h[i+2]... h[j]) min(h[i],h[i+1],h[i+2]...h[j]),很明显这样求时间复杂度太大,所以我们可以考虑换个角度考虑,因为如果 h [ k ] h[k] h[k]是一段 m i n ( h [ i ] , h [ i + 1 ] , h [ i + 2 ] . . . h [ j ] ) min(h[i] ,h[i+1],h[i+2]... h[j]) min(h[i],h[i+1],h[i+2]...h[j]) 的最小值,那么我们认为h[k]产生了贡献值。这样就可以用单调栈维护啦!

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e6+50;
const int INF = 0x3f3f3f3f;
int a[MAXN],c[MAXN],rk[MAXN],y[MAXN],sa[MAXN],h[MAXN];
int l[MAXN],r[MAXN];
char s[MAXN];
inline void SA(int n,int m){
    for(int i=1;i<=n;i++) rk[i]=s[i],++c[rk[i]];
    for(int i=1;i<=m;i++) c[i]+=c[i-1];
    for(int i=n;i>=1;i--) sa[c[rk[i]]--]=i;
    for(int k=1;k<=n;k<<=1){
        int num = 0;
        for(int i=n-k+1;i<=n;i++) y[++num]=i;
        for(int i=1;i<=n;i++)
            if(sa[i]>k)
                y[++num]=sa[i]-k;
        for(int i=1;i<=m;i++) c[i]=0;
        for(int i=1;i<=n;i++) ++c[rk[i]];
        for(int i=1;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i>=1;i--)
            sa[c[rk[y[i]]]--]=y[i];
        swap(rk,y);
        rk[sa[1]] = num = 1;
        for(int i=2;i<=n;i++)
            rk[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? num : ++num);
        if(num==n) break;
        m = num;
    }
    for(int i=1;i<=n;i++) rk[sa[i]] = i;
    int k = 0;
    for(int i=1;i<=n;i++){
        if(rk[i]==1) continue;
        if(k) --k;
        int j = sa[rk[i]-1];
        while(j+k<=n && i+k<=n && s[j+k]==s[i+k]) ++k;
        h[rk[i]] = k;
    }
}
int main(){
    scanf("%s",s+1);
    int n = strlen(s+1),m = 'z';
    SA(n,m);
    h[0] = h[n+1] = -INF;
    LL res = 0;
    for(int i=1;i<=n;i++) l[i]=i-1,r[i]=i+1;
    for(int i=2;i<=n;i++) while(h[l[i]]>h[i]) l[i]=l[l[i]];
    for(int i=n;i>=2;i--) while(h[r[i]]>=h[i]) r[i]=r[r[i]];
    for(int i=2;i<=n;i++) res += 2LL*h[i]*(r[i]-i)*(i-l[i]);
    printf("%lld\n",1LL*(n+1)*(n-1)*n/2-res);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,我似乎没有找到与彩旗飘飘Python相关的引用内容。但是,如果你想在Python中实现彩旗飘飘的效果,你可以使用Pygame库来创建动画。以下是一个简单的示例代码,可以帮助你开始: ```python import pygame import random # 初始化pygame pygame.init() # 设置屏幕尺寸 screen_width = 800 screen_height = 600 screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("彩旗飘飘") # 加载彩旗图片 flag_image = pygame.image.load("flag.png") # 获取彩旗图片的宽度和高度 flag_width = flag_image.get_width() flag_height = flag_image.get_height() # 设置彩旗的起始位置 flag_x = screen_width // 2 - flag_width // 2 flag_y = screen_height // 2 - flag_height // 2 # 设置彩旗的速度 flag_speed_x = random.randint(-5, 5) flag_speed_y = random.randint(-5, 5) # 游戏主循环 running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 移动彩旗 flag_x += flag_speed_x flag_y += flag_speed_y # 如果彩旗碰到边界,反转速度方向 if flag_x <= 0 or flag_x + flag_width >= screen_width: flag_speed_x = -flag_speed_x if flag_y <= 0 or flag_y + flag_height >= screen_height: flag_speed_y = -flag_speed_y # 清空屏幕 screen.fill((255, 255, 255)) # 绘制彩旗 screen.blit(flag_image, (flag_x, flag_y)) # 更新屏幕 pygame.display.flip() # 退出游戏 pygame.quit() ``` 请注意,你需要将代码中的"flag.png"替换为你自己的彩旗图片。此外,你还可以根据需要调整彩旗的起始位置、速度等参数。希望这个示例代码能帮助到你!<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [3d max制作彩旗飘飘](https://download.csdn.net/download/m0_71585230/88011500)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [[Rqnoj-371][AHOI1997]彩旗飘飘](https://blog.csdn.net/w745241408/article/details/7176600)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值