xsy3320 string

题意:

​ 给一颗树,每条边上有一个字符,求有多少条路径是回文的。(\(N\leq50000\),\(c\in\{1,2\}\))


题解:

​ 前置芝士:回文前缀&&border

​ 先点分治,问题变成求经过分治中心的回文路径个数。

​ 观察这样的路径长啥样:u - S -> rt <- T - k <- S -v。

​ 其中\((u,v)\)是一条回文路径,T是回文串,\(v\)到\(k\)的串与\(u\)到\(rt\)的串相同。

​ 判断一个从根到某个点的路径是不是回文路径可以用哈希解决。建出ac自动机,在fail树上找出每一个串的回文前缀。
根据border理论,一个回文串的回文前缀可以被划分成\(O(\log n)\)个不同的等差数列。每次在fail树上枚举\(v\),在暴力找它所有回文前缀,找S的个数,问题变成了给你一段区间,问里面 $ len\mod d $ 为某个值的所有串的个数。考虑怎么处理这样的询问。发现可以差分区间,然后对每种公差,存所有余数的答案。但是这样的空间复杂度是\(O(n^2)\)的。所以分块处理,对于公差大于\(\sqrt {siz}\)的,暴力往上跳统计答案。

​ 可以发现最差情况下ac自动机和原树同构,分块数组是\(O(\sqrt n^2=n)\)的,所以空间复杂度\(O(n)\)。

​ 对于每一个点,跳回文前缀是\(O(\log n)\)的,对于公差大于\(\sqrt n\)的,暴力跳是\(\sqrt n\)的,小于的要在链上二分,所以这一部分的时间复杂度是\(O(n\sqrt n+n\log^2 n)\)。处理询问时要\(O(\sqrt n)\)保存答案时间,所以这里复杂度是\(O(n\sqrt n)\)。加上每次分治的复杂度,总复杂度:
\[
T(n)=2T(\frac n2)+O(n\sqrt n)=O(n\sqrt n)
\]
​ 可以通过此题。

代码:

#include<bits/stdc++.h>
#define fo(i,l,r) for(int i=l;i<=r;i++)
#define of(i,l,r) for(int i=l;i>=r;i--)
#define fe(i,u) for(int i=head[u];i;i=e[i].next)
#define el putchar('\n')
#define ta putchar('    ')
using namespace std;
typedef long long ll;
inline void open(const char *s)
{
    #ifndef ONLINE_JUDGE
    char str[20];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
    #endif
}
const int NS=(1<<21)+5;
char buf[NS],*H,*T;
inline char Get()
{
    if(H==T)T=(H=buf)+fread(buf,1,NS,stdin);
    if(H==T)return -1;return *H++;
}
inline int rd()
{
    static int x,f;
    x=0;f=1;
    char ch=Get();
    for(;ch<'0'||ch>'9';ch=Get())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=Get())x=x*10+ch-'0';
    return f>0?x:-x;
}
const int N=50010;
const int mod1=1000000007;
const int mod2=998244353;
struct edge{
    int v,w,next;
    edge(int v=0,int w=0,int next=0):v(v),w(w),next(next){}
}e[N<<1];
int n,bin1[N],bin2[N];
int tot=0,head[N];
int siz[N],s,root;
ll ans=0,res;
bool vis[N];
int B;

struct node{int d,m,siz,ty;};
vector<node>ask[N];
vector<int>g[N];

inline void add(int u,int v,int w){e[++tot]=edge(v,w,head[u]);head[u]=tot;}
inline void pre(int n){bin1[0]=bin2[0]=1;fo(i,1,n)bin1[i]=3ll*bin1[i-1]%mod1,bin2[i]=3ll*bin2[i-1]%mod2;}

namespace AC{
struct tree{
    int ch[2],siz,fail,revfa,len,anc;bool rev;
    tree(){ch[0]=ch[1]=siz=fail=revfa=len=anc=rev=0;}
}tr[N];int rt,cnt=0;
queue<int>q;

void buildfail()
{
    while(!q.empty())q.pop();
    q.push(rt);tr[rt].fail=rt;
    while(!q.empty()){
        int u=q.front();q.pop();
        fo(i,0,1){
            int v=tr[u].ch[i];
            if(!v){tr[u].ch[i]=u==rt?u:tr[tr[u].fail].ch[i];continue;}
            if(tr[u].rev)tr[v].revfa=u;else tr[v].revfa=tr[u].revfa;
            tr[v].fail=u==rt?u:tr[tr[u].fail].ch[i];q.push(v);
        }
    }
    fo(i,2,cnt)
        if(tr[i].len-tr[tr[i].revfa].len==tr[tr[i].revfa].len-tr[tr[tr[i].revfa].revfa].len)
            tr[i].anc=tr[tr[i].revfa].anc;
            else tr[i].anc=tr[i].revfa;
    fo(i,2,cnt)g[tr[i].fail].push_back(i);
}

}

void getsiz(int u,int fat){siz[u]=1;fe(i,u){int v=e[i].v;if(v==fat||vis[v])continue;getsiz(v,u);siz[u]+=siz[v];}}
void getrt(int u,int fat){int mx=siz[0]-siz[u];fe(i,u){int v=e[i].v;if(v==fat||vis[v])continue;mx=max(mx,siz[v]);getrt(v,u);}if(mx<s)s=mx,root=u;}
void dfs1(int u,int fat,int &o,int h1,int rh1,int h2,int rh2,int dep)
{using namespace AC;
    if(!o){tr[o=++cnt]=tree();tr[o].len=dep;tr[o].rev=h1==rh1&&h2==rh2;}
    ++B;++tr[o].siz;
    fe(i,u){
        int v=e[i].v,w=e[i].w;if(v==fat||vis[v])continue;
        int nh1=(3ll*h1%mod1+w)%mod1;
        int nrh1=((ll)bin1[dep]*w+rh1)%mod1;
        int nh2=(3ll*h2%mod2+w)%mod2;
        int nrh2=((ll)bin2[dep]*w+rh2)%mod2;
        dfs1(v,u,tr[o].ch[w&1],nh1,nrh1,nh2,nrh2,dep+1);
    }
}

int sum[N],st[N],top=0,lst[N];
int f[350][350];
bool hhhhh=1;
void getq(int u)
{using namespace AC;
    st[++top]=tr[u].len;lst[top]=u;
    res+=(ll)tr[u].siz*(tr[u].siz-1)>>1;
    int x=tr[u].revfa;if(tr[u].rev)x=u;
    sum[tr[u].len]+=tr[u].siz;
    while(tr[x].len){
        int d=tr[x].len-tr[tr[x].revfa].len;
        int L=tr[x].len-tr[tr[x].anc].len;
        if(d<=B){
            int l=tr[u].len-tr[x].len,r=tr[u].len-tr[x].len+L-d;
            l=lower_bound(st+1,st+top+1,l)-st;r=upper_bound(st+1,st+top+1,r)-st-1;
            if(l-1)ask[lst[l-1]].push_back(node{d,(tr[u].len-tr[x].len)%d,tr[u].siz,-1});
            if(r)ask[lst[r]].push_back(node{d,(tr[u].len-tr[x].len)%d,tr[u].siz,1});
        }else for(int t=x;t!=tr[x].anc;t=tr[t].revfa)res+=(ll)tr[u].siz*sum[tr[u].len-tr[t].len];
        x=tr[x].anc;
    }
    fo(i,0,(int)g[u].size()-1)getq(g[u][i]);
    --top;sum[tr[u].len]-=tr[u].siz;
}
void getans(int u)
{using namespace AC;
    fo(i,1,B)f[i][tr[u].len%i]+=tr[u].siz;
    fo(i,0,(int)ask[u].size()-1)res+=f[ask[u][i].d][ask[u][i].m]*ask[u][i].siz*ask[u][i].ty;
    fo(i,0,(int)g[u].size()-1)getans(g[u][i]);
    fo(i,1,B)f[i][tr[u].len%i]-=tr[u].siz;
}

inline ll calc(int u,int w)
{
    using namespace AC;
    res=0;
    B=0;
    if(w){
        rt=cnt=1;tr[rt]=tree();tr[rt].rev=1;
        dfs1(u,0,tr[rt].ch[w&1],w,w,w,w,1);
    }
    else rt=cnt=0,dfs1(u,0,rt,0,0,0,0,0);
    B=floor(sqrt(B));
    AC::buildfail();
    getq(rt);getans(rt);
    fo(i,1,cnt)g[i].clear(),ask[i].clear();
    return res;
}

void gao(int u)
{
    getsiz(u,0);siz[0]=s=siz[u];root=0;getrt(u,0);vis[u=root]=1;ans+=calc(u,0);
    fe(i,u){
        int v=e[i].v,w=e[i].w;if(vis[v])continue;
        ans-=calc(v,w);gao(v);
    }
}

int main()
{
    open("c");
    n=rd();
    fo(i,2,n){
        int x=rd(),y=rd(),z=2-rd();
        add(x,y,z);add(y,x,z);
    }
    pre(n);gao(1);
    printf("%lld\n",ans);
    return 0;
}

好写好调,没细节。

xsy3320 string的更多相关文章

  1. 【XSY3320】string AC自动机 哈希 点分治

    题目大意 给一棵树,每条边上有一个字符,求有多少对 \((x,y)(x<y)\),满足 \(x\) 到 \(y\) 路径上的边上的字符按顺序组成的字符串为回文串. \(1\leq n\leq 5 ...

  2. 透过WinDBG的视角看String

    摘要 : 最近在博客园里面看到有人在讨论 C# String的一些特性. 大部分情况下是从CODING的角度来讨论String. 本人觉得非常好奇, 在运行时态, String是如何与这些特性联系上的 ...

  3. JavaScript String对象

    本编主要介绍String 字符串对象. 目录 1. 介绍:阐述 String 对象的说明以及定义方式. 2. 实例属性:介绍 String 对象的实例属性: length. 3. 实例方法:介绍 St ...

  4. ElasticSearch 5学习(9)——映射和分析(string类型废弃)

    在ElasticSearch中,存入文档的内容类似于传统数据每个字段一样,都会有一个指定的属性,为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成字符串值,Elasticsearc ...

  5. [C#] string 与 String,大 S 与小 S 之间没有什么不可言说的秘密

    string 与 String,大 S 与小 S 之间没有什么不可言说的秘密 目录 小写 string 与大写 String 声明与初始化 string string 的不可变性 正则 string ...

  6. js报错: Uncaught RangeError: Invalid string length

    在ajax请求后得到的json数据,遍历的时候chrome控制台报这个错误:Uncaught RangeError: Invalid string length,在stackoverflow查找答案时 ...

  7. c# 字符串连接使用“+”和string.format格式化两种方式

    参考文章:http://www.liangshunet.com/ca/201303/218815742.htm 字符串之间的连接常用的两种是:“+”连接.string.format格式化连接.Stri ...

  8. 【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

    之前以为BinaryWriter写string会严格按构造时指定的编码(不指定则是无BOM的UTF8)写入string的二进制,如下面的代码: //将字符串"a"写入流,再拿到流的 ...

  9. JavaScript中String对象的方法介绍

    1.字符方法 1.1 charAt() 方法,返回字符串中指定位置的字符. var question = "Do you like JavaScript?"; alert(ques ...

随机推荐

  1. sql中--行处理数据的两种方式

    --创建待使用的表格CREATE TABLE Orders ( OrderID INT , CostValue DECIMAL(18, 2) );WITH cte_temp AS ( SELECT 1 ...

  2. 关于element-ui框架里面table组件的使用

    1.表格的数据放哪里:后台返回的列表数据放到:data后面. 2.每一个列怎么显示数据:返回的列表里面的属性想要在列显示出来,将对应的字段名写到prop后面就行. 3.列表上面想显示序号:{{scop ...

  3. 【J-meter】正则表达式提取

    当获取的值中含有折行,可采用下面的办法解决:

  4. python中的装饰器decorator

    python中的装饰器 装饰器是为了解决以下描述的问题而产生的方法 我们在已有的函数代码的基础上,想要动态的为这个函数增加功能而又不改变原函数的代码 例如有三个函数: def f1(x): retur ...

  5. 【Codeforces Beta Round #45 D】Permutations

    [题目链接]:http://codeforces.com/problemset/problem/48/D [题意] 给你n个数字; 然后让你确定,这n个数字是否能由若干个(1..x)的排列连在一起打乱 ...

  6. Select For update语句浅析

    Select -forupdate语句是我们经常使用手工加锁语句.通常情况下,select语句是不会对数据加锁,妨碍影响其他的DML和DDL操作.同时,在多版本一致读机制的支持下,select语句也不 ...

  7. 做一个萌萌哒的button之box-shadow

    接上篇:http://blog.csdn.net/u010037043/article/details/47035077 一.box-shadow box-shadow是给元素块加入周边阴影效果. b ...

  8. [React] Pass a function to setState in React

    In React, when you want to set the state which calculation depends on the current state, using an ob ...

  9. CodeForces 570B Simple Game 概率

    原题: http://codeforces.com/contest/570/problem/B 题目: Simple Game time limit per test1 second memory l ...

  10. CentOS 与Ubuntu 安装软件包的对比

    工作需要开始转向centos,简单记录软件包安装 wget不是安装方式 他是一种下载软件类似与迅雷 如果要下载一个软件 我们可以直接 wget 下载地址 ap-get是ubuntu下的一个软件安装方式 ...