BZOJ 3879: SvT 虚树 + 后缀自动机

Description

(我并不想告诉你题目名字是什么鬼)

有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].

现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.

 Input

第一行两个正整数n,m,分别表示S的长度以及询问的次数.

接下来一行有一个字符串S.

接下来有m组询问,对于每一组询问,均按照以下格式在一行内给出:

首先是一个整数t,表示共有多少个后缀.接下来t个整数分别表示t个后缀在字符串S中的出现位置.

Output

对于每一组询问,输出一行一个整数,表示该组询问的答案.由于答案可能很大,仅需要输出这个答案对于23333333333333333(一个巨大的质数)取模的余数.

题解:和CF的一个题几乎一模一样,都是利用虚树统计答案

#include <bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout) 
#define maxn 1200004 
#define mod 23333333333333333
#define ll long long 
using namespace std;  
int edges,n,Q; 
int hd[maxn],to[maxn],nex[maxn],tr[maxn];   
char str[maxn]; 
void addedge(int u,int v,int c)                     //1001 个
{ 
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; 
}
namespace SAM
{  
    int last, tot; 
    int len[maxn<<1],f[maxn<<1],trans[maxn<<1][27];  
    void init() { last=tot=1; } 
    int extend(int c) 
    {
        int np=++tot,p=last; 
        len[np]=len[p]+1,last=np; 
        while(p&&!trans[p][c]) trans[p][c]=np, p=f[p]; 
        if(!p) f[np]=1; 
        else
        {
            int q=trans[p][c]; 
            if(len[q]==len[p]+1) f[np]=q; 
            else
            {
                int nq=++tot; 
                len[nq]=len[p]+1; 
                memcpy(trans[nq],trans[q],sizeof(trans[q])); 
                f[nq]=f[q],f[q]=f[np]=nq;    
                while(p&&trans[p][c]==q) trans[p][c]=nq,p=f[p]; 
            }
        }  
        return np; 
    } 
    void build() 
    {
        for(int i=2;i<=tot;++i) addedge(f[i],i,len[f[i]]-len[i]); 
    }
}  
int tim; 
int dfn[maxn],Top[maxn],hson[maxn],siz[maxn],fa[maxn],dis[maxn];   
void dfs1(int u,int ff)
{
    fa[u]=ff,siz[u]=1,dfn[u]=++tim,dis[u]=dis[ff]+1; 
    for(int i=hd[u];i;i=nex[i])
    {
        int v=to[i]; 
        if(v==ff) continue;  
        dfs1(v,u); 
        siz[u]+=siz[v]; 
        if(siz[v]>siz[hson[u]]) hson[u]=v; 
    }
}
void dfs2(int u,int tp)
{
    Top[u]=tp; 
    if(hson[u]) dfs2(hson[u],tp); 
    for(int i=hd[u];i;i=nex[i])
    {
        int v=to[i]; 
        if(v==fa[u]||v==hson[u]) continue; 
        dfs2(v,v); 
    }
}
int LCA(int x,int y)
{
    while(Top[x]!=Top[y]) 
    {
        dis[Top[x]]>dis[Top[y]]?x=fa[Top[x]]:y=fa[Top[y]]; 
    }
    return dis[x]<dis[y]?x:y; 
} 
vector<int>G[maxn<<1]; 
ll ans=0; 
int t=0; 
int A[maxn<<1],mk[maxn<<1],size[maxn<<1],S[maxn<<1];         
bool cmp(int i,int j) 
{
    return dfn[i]<dfn[j]; 
}
void addvir(int x,int y)
{  
    G[x].push_back(y); 
}
void insert(int x)
{
    if(t<=1) { S[++t]=x; return; }
    int lca=LCA(S[t],x); 
    if(lca==S[t]) { S[++t]=x; return; } 
    while(t>1&&dis[S[t-1]]>=dis[lca]) addvir(S[t-1],S[t]),--t; 
    if(S[t]!=lca) addvir(lca,S[t]),S[t]=lca; 
    S[++t]=x;   
} 
void DP(int x) 
{ 
    size[x]=mk[x]; 
    for(int i=0;i<G[x].size();++i) 
    {
        DP(G[x][i]); 
        ans+=1ll*size[x]*size[G[x][i]]%mod*SAM::len[x]%mod;  
        ans%=mod; 
        size[x]+=size[G[x][i]]; 
    }
    G[x].clear(); 
    mk[x]=0; 
} 
void work() 
{ 
    int k,a; 
    scanf("%d",&k); 
    for(int i=1;i<=k;++i) scanf("%d",&a), A[i]=tr[a], mk[A[i]]=1;           
    sort(A+1,A+1+k,cmp);            
    k=unique(A+1,A+1+k)-(A+1);         
    t=ans=0;    
    if(A[1]!=1) S[++t]=1;                            
    for(int i=1;i<=k;++i) insert(A[i]);   
    while(t>1) addvir(S[t-1],S[t]),--t;     
    DP(1);     
    printf("%lld\n",ans);    
}
int main() 
{
    // setIO("input");   
    scanf("%d%d%s",&n,&Q,str+1); 
    SAM::init();      
    for(int i=n;i>=1;--i) tr[i]=SAM::extend(str[i]-'a');    
    SAM::build();      
    dis[1]=1,dfs1(1,0),dfs2(1,1);        
    for(int i=1;i<=Q;++i) 
    {  
        work();   
    }
    return 0; 
}

  

转载于:https://www.cnblogs.com/guangheli/p/11133043.html

### 光流法C++源代码解析与应用 #### 光流法原理 光流法是一种在计算机视觉领域中用于追踪视频序列中运动物体的方法。它基于亮度不变性假设,即场景中的点在时间上保持相同的灰度值,从而通过分析连续帧之间的像素变化来估计运动方向和速度。在数学上,光流场可以表示为像素位置和时间的一阶导数,即Ex、Ey(空间梯度)和Et(时间梯度),它们共同构成光流方程的基础。 #### C++实现细节 在给定的C++源代码片段中,`calculate`函数负责计算光流场。该函数接收一个图像缓冲区`buf`作为输入,并初始化了几个关键变量:`Ex`、`Ey`和`Et`分别代表沿x轴、y轴和时间轴的像素强度变化;`gray1`和`gray2`用于存储当前帧和前一帧的平均灰度值;`u`则表示计算出的光流矢量大小。 #### 图像处理流程 1. **初始化和预处理**:`memset`函数被用来清零`opticalflow`数组,它将保存计算出的光流数据。同时,`output`数组被填充为白色,这通常用于可视化结果。 2. **灰度计算**:对每一像素点进行处理,计算其灰度值。这里采用的是RGB通道平均值的计算方法,将每个像素的R、G、B值相加后除以3,得到一个近似灰度值。此步骤确保了计算过程的鲁棒性和效率。 3. **光流向量计算**:通过比较当前帧和前一帧的灰度值,计算出每个像素点的Ex、Ey和Et值。这里值得注意的是,光流向量的大小`u`是通过`Et`除以`sqrt(Ex^2 + Ey^2)`得到的,再乘以10进行量化处理,以减少计算复杂度。 4. **结果存储与阈值处理**:计算出的光流值被存储在`opticalflow`数组中。如果`u`的绝对值超过10,则认为该点存在显著运动,因此在`output`数组中将对应位置标记为黑色,形成运动区域的可视化效果。 5. **状态更新**:通过`memcpy`函数将当前帧复制到`prevframe`中,为下一次迭代做准备。 #### 扩展应用:Lukas-Kanade算法 除了上述基础的光流计算外,代码还提到了Lukas-Kanade算法的应用。这是一种更高级的光流计算方法,能够提供更精确的运动估计。在`ImgOpticalFlow`函数中,通过调用`cvCalcOpticalFlowLK`函数实现了这一算法,该函数接受前一帧和当前帧的灰度图,以及窗口大小等参数,返回像素级别的光流场信息。 在实际应用中,光流法常用于目标跟踪、运动检测、视频压缩等领域。通过深入理解和优化光流算法,可以进一步提升视频分析的准确性和实时性能。 光流法及其C++实现是计算机视觉领域的一个重要组成部分,通过对连续帧间像素变化的精细分析,能够有效捕捉和理解动态场景中的运动信息
微信小程序作为腾讯推出的一种轻型应用形式,因其便捷性与高效性,已广泛应用于日常生活中。以下为该平台的主要特性及配套资源说明: 特性方面: 操作便捷,即开即用:用户通过微信内搜索或扫描二维码即可直接使用,无需额外下载安装,减少了对手机存储空间的占用,也简化了使用流程。 多端兼容,统一开发:该平台支持在多种操作系统与设备上运行,开发者无需针对不同平台进行重复适配,可在一个统一的环境中完成开发工作。 功能丰富,接口完善:平台提供了多样化的API接口,便于开发者实现如支付功能、用户身份验证及消息通知等多样化需求。 社交整合,传播高效:小程序深度嵌入微信生态,能有效利用社交关系链,促进用户之间的互动与传播。 开发成本低,周期短:相比传统应用程序,小程序的开发投入更少,开发周期更短,有助于企业快速实现产品上线。 资源内容: “微信小程序-项目源码-原生开发框架-含效果截图示例”这一资料包,提供了完整的项目源码,并基于原生开发方式构建,确保了代码的稳定性与可维护性。内容涵盖项目结构、页面设计、功能模块等关键部分,配有详细说明与注释,便于使用者迅速理解并掌握开发方法。此外,还附有多个实际运行效果的截图,帮助用户直观了解功能实现情况,评估其在实际应用中的表现与价值。该资源适用于前端开发人员、技术爱好者及希望拓展业务的机构,具有较高的参考与使用价值。欢迎查阅,助力小程序开发实践。资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值