【CF套题】Codeforces Round #459 (Div. 1) (917A~E)

Div1的前三题和NOIP提高组可能难度差不多(或菜一点),就拿来做模拟了。
D和E当作练习,然后这个E真的是打的我心态爆炸(最后弃疗了)。
原题地址
为什么Markdown没有折叠代码片的功能啊!

A.The Monster

【题目大意】
给定一个含有’(‘,’)’,’?’的字符串,其中’?’必须代替其他两种字符的一中。问有多少个区间[l,r]满足这个区间可以是一个合法的括号序列。

【解题思路】
考虑一段区间 [l,r] [ l , r ] 是合法的充要条件:
1.[l,l],[l,l+1]...[l,r] 1. [ l , l ] , [ l , l + 1 ] . . . [ l , r ] 每个区间满足左括号数+问号数>=右括号数
2.[r,r],[r1,r]...[l,r] 2. [ r , r ] , [ r − 1 , r ] . . . [ l , r ] 每个区间满足右括号数+问号数>=左括号数
两重循环扫即可。

#include<bits/stdc++.h>
using namespace std;

const int N=5005;
int n,cl,cr,ct,ans;
int vis[N][N];
char s[N];

void trans(char c)
{
    if(c=='(') ++cl;
    else if(c==')') ++cr;
    else ++ct;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("CF917A.in","r",stdin);
    freopen("CF917A.out","w",stdout);
#endif
    scanf("%s",s+1);n=strlen(s+1);
    for(int i=1;i<=n;++i)
    {
        cl=cr=ct=0;
        for(int j=i;j<=n;j++)
        {
            trans(s[j]);
            if(cl+ct<cr) break;
            ++vis[i][j];
        }   
    }
    for(int i=n;i;--i)
    {
        cl=cr=ct=0;
        for(int j=i;j;--j)
        {
            trans(s[j]);
            if(cr+ct<cl) break;
            ++vis[j][i];
        }
    }
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j)
            if(vis[i][j]==2 && (j-i)%2) ++ans;
    printf("%d\n",ans);

    return 0;
}

B.MADMAX

【题目大意】
两个人在一幅带小写字母权值的有向图上,分别 i i j出发(可以相同)。轮流行动,每次所走的边权必须大于等于上一个人所走的边权,第一个无法走的人失败另一个人获胜,输出所有起点 i i j的获胜情况。

【解题思路】
f[i][j][las] f [ i ] [ j ] [ l a s ] 为先手在 i i ,后手在j,后手上一步走的是 las l a s
枚举两个人的起点爆搜即可。

#include<bits/stdc++.h>
using namespace std;

const int N=105;
int n,m,tot;
int head[N],f[N][N][28];

struct Tway{int v,w,nex;}e[N*N];
void add(int u,int v,int w) {e[++tot]=(Tway){v,w,head[u]};head[u]=tot;}

int dfs(int x,int y,int las)
{
    if(~f[x][y][las]) return f[x][y][las];
    for(int i=head[x];i;i=e[i].nex)
    {
        int v=e[i].v,w=e[i].w,t=-1;
        if(w>=las) t=dfs(y,v,w);
        if(!t) f[x][y][las]=1;
    }
    if(!~f[x][y][las]) f[x][y][las]=0;
    return f[x][y][las];
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("CF917B.in","r",stdin);
    freopen("CF917B.out","w",stdout);
#endif  
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        int u,v;char s[2];
        scanf("%d%d%s",&u,&v,s);
        add(u,v,s[0]-'a'+1);
    }
    memset(f,-1,sizeof(f));
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            dfs(i,j,0);
    for(int i=1;i<=n;++i,puts(""))
        for(int j=1;j<=n;++j)
            putchar(f[i][j][0]?'A':'B');

    return 0;
}

C.Pollywog

【题目大意】
有一行 n n 个石头,x只青蛙要从最左边的 x x 个石头跳到最右边的x个石头上。每次只能最左边的蛙跳不超过 k k 的距离,跳i距离花费 ci c i ,一块石头上不能同时站两只蛙。其中还有有 q q 个特殊石头,跳到上面会增加wi花费(可能是负数)。求最小花费。 xk8,n1e8 x ≤ k ≤ 8 , n ≤ 1 e 8

【解题思路】
如果没有 q q 个特殊石头的限制,因为K很小,我们可以状压一个右边的 K K 个石头,因为一共x个蛙,那么转移是 Cxkk C k x ∗ k 的,这里最多560.
观察转移的形式,实际上就是一个(min,+)矩阵乘法。
那么限制有 q q 个特殊石头也不难想到,一个特殊石头只能影响前后K个石头,那么我们只需要在这些地方“刹车”,暴力处理这部分的答案即可。

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
const int N=75,S=(1<<8);
const LL INF=(LL)(1ll<<61);
int X,K,n,m,tot,now;
int cnt[S],mp[S],id[N],c[N];
LL sum;
pii p[N];

struct Matrix
{
    LL a[N][N];
    Matrix() {for(int i=0;i<N;a[i][i]=0,++i) for(int j=0;j<N;++j) a[i][j]=INF;}
    void init() {for(int i=1;i<=tot;++i) for(int j=0;j<N;++j) a[i][j]=INF;}
}ans,a;

Matrix mul(Matrix x,Matrix y)
{
    Matrix ret;ret.init();
    for(int i=1;i<=tot;++i)
        for(int k=1;k<=tot;++k)
            for(int j=1;j<=tot;++j)
                ret.a[i][j]=min(ret.a[i][j],x.a[i][k]+y.a[k][j]);
    return ret;
}

Matrix qpow(Matrix x,int y)
{
    Matrix ret;
    for(;y;y>>=1,x=mul(x,x)) if(y&1) ret=mul(ret,x);
    return ret;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("CF917C.in","r",stdin);
    freopen("CF917C.out","w",stdout);
#endif  
    scanf("%d%d%d%d",&X,&K,&n,&m);
    for(int i=1;i<=K;++i) scanf("%d",&c[i]);
    for(int i=1;i<(1<<K);++i) 
    {
        cnt[i]=cnt[i>>1]+(i&1);
        if(cnt[i]==X) mp[i]=++tot,id[tot]=i; 
    }
    for(int i=1;i<=tot;++i) for(int j=1;j<=tot;++j) a.a[i][j]=INF;
    for(int i=1;i<=tot;++i)
    {
        if(id[i]&1)
        {
            for(int j=1;j<=K;++j)
                if(!(id[i]&(1<<j))) a.a[i][mp[(id[i]^(1<<j))>>1]]=c[j];
        }
        else a.a[i][mp[id[i]>>1]]=0;
    }
    for(int i=1;i<=m;++i) scanf("%d%d",&p[i].fi,&p[i].se);
    sort(p+1,p+m+1);

    now=1;
    for(int i=1;i<=m;++i)
    {
        if(p[i].fi>=n-X+1) {sum+=p[i].se;continue;}
        ans=mul(ans,qpow(a,p[i].fi-now));now=p[i].fi;
        for(int j=1;j<=tot;++j) if(id[j]&1)
            for(int k=1;k<=tot;++k) ans.a[k][j]+=p[i].se;
    }
    ans=mul(ans,qpow(a,n-X-now+1)); sum+=ans.a[1][1];
    printf("%lld\n",sum);

    return 0;
}

D.Stranger Trees

【题目大意】给出一棵 n n 个节点的生成树,问n个节点的完全图的所有生成树中,和这棵生成树恰好有 i i 条边重合的答案,输出i=1...n的所有答案。

【解题思路】
设树为 T T ,从暴力出发,在完全图中,我们枚举可能选的T K K 条边,然后屏蔽掉剩下的T的边,在基尔霍夫矩阵上随便搞一个余子式,即可求出剩下的图的生成树个数。此时,求出的是相似度至多为 K K 的方案。
考虑带权生成树计数的做法,此时我们的基尔霍夫矩阵不再是度数矩阵减邻接矩阵,而是边权和矩阵减去边权矩阵。这里一颗生成树的贡献是边权的乘积。
类似的,对于T的边,标记其边权为 x x ,否则标记为1。仍然对他求行列式,可以得出一个多项式,发现多项式i次项的系数,就是相似度为i时的生成树方案。
由于不能暴力求含未知数的行列式,我们将 x x n个常数进去算,然后得出点值再拉格朗日插值回来或者高斯消元,也可以算出答案。
时间复杂度 O(n4) O ( n 4 )

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=105,mod=1e9+7;
int n,len;
LL inv[N],fac[N],ifac[N],ans[N],p[N];
LL a[N][N],mp[N][N];

LL qpow(LL x,LL y)
{
    LL ret=1;
    for(;y;y>>=1,x=x*x%mod) if(y&1) ret=ret*x%mod;
    return ret;
}

void up(LL &x,LL y) {x+=y;if(x>=mod)x-=mod;if(x<0)x+=mod;}

LL gauss(int n)
{
    LL ret=1,inp;
    for(int i=1,id;i<=n;++i)
    {
        for(id=i;id<=n && !a[id][i];++id);
        if(id>n) return 0;
        if(id^i) { for(int j=1;j<=n;++j) swap(a[i][j],a[id][j]); ret=-ret;}
        ret=ret*a[i][i]%mod;inp=qpow(a[i][i],mod-2);
        for(int j=i+1;j<=n;++j)
        {
            if(!a[j][i]) continue;
            LL tmp=a[j][i]*inp%mod;
            for(int k=i;k<=n;++k) up(a[j][k],-a[i][k]*tmp%mod);
        }
    }
    up(ret,mod);
    return ret; 
}

void calc(int x)
{
    memset(a,0,sizeof(a));
    for(int i=1;i<n;++i)
        for(int j=i+1;j<=n;++j)
        {
            LL t=mp[i][j]?x:1;
            a[i][j]-=t;a[j][i]-=t;a[i][i]+=t;a[j][j]+=t;
        }
    //for(int i=1;i<=n;++i,puts("")) for(int j=1;j<=n;++j) printf("%d ",a[i][j]);puts("");
    p[len=0]=gauss(n-1)*ifac[x-1]%mod*ifac[n-x]%mod;
    if((n-x)&1) p[0]=mod-p[0];
    for(int i=1;i<=n;++i)
    {
        if(x==i) continue;
        LL t=mod-i;
        for(int j=++len;~j;--j)    p[j]=(t*p[j]+(j?p[j-1]:0))%mod;
    }
    //for(int i=0;i<=len;++i) printf("%d ",p[i]);puts("");
    for(int i=0;i<=len;++i) up(ans[i],p[i]),p[i]=0;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("CF917D.in","r",stdin);
    freopen("CF917D.out","w",stdout);
#endif
    scanf("%d",&n);fac[0]=ifac[0]=1;
    for(int i=1;i<=n;++i) inv[i]=qpow(i,mod-2),fac[i]=fac[i-1]*i%mod,ifac[i]=ifac[i-1]*inv[i]%mod;
    for(int i=1;i<n;++i)
    {
        int u,v;scanf("%d%d",&u,&v);
        mp[u][v]=mp[v][u]=1;
    }
    for(int i=1;i<=n;++i) calc(i);
    for(int i=0;i<n;++i) printf("%lld ",ans[i]);

    return 0;
}

E.Upside Down

【题目大意】
出题人:我就是要毒瘤毒瘤毒瘤
给定一棵 n n 个节点带小写字母边权的树,再给出m个字符串, q q 个询问u>v路径形成的字符串中,第 i i 个字符串出现了多少次(开头位置不同计数,如ababa aba a b a 出现了2次)。

【解题思路】
毒瘤题!写过这么多题中,最毒瘤的一道。建议先去看金策的论文《字符串算法选讲》,里面有很多有意思的性质。
首先单独考虑一个询问,设 f f lca(u,v),先一个串在考虑 u>f u − > f f>v f − > v 的出现次数。
我们可以对这 m m 个串以及它们的反串建AC自动机。
那么若Si或其反串)在一个位置u到根节点的路径形成的串中作为一个后缀,则u在自动机上的位置在fail树的 Si S i 的子树内。因此这部分我们可以用线段树或者BIT维护dfs序。

重点在于考虑 u>f>v u − > f − > v 的贡献,那么 Si S i 一定覆盖在 u>f u − > f 的后半路径和 f>v f − > v 的前半路径,即分别以前缀和后缀的形式出现。接下来我们只需要知道 u>f u − > f 的一个 border b o r d e r 能匹配的最长前缀和 f>v f − > v border b o r d e r 匹配的最长后缀即可。

引理:对于 kmp k m p fail f a i l 链( x,failx,failfailx,...,0 x , f a i l x , f a i l f a i l x , . . . , 0 ) ,会形成 logx l o g x 个等差数列。

这个在金策的论文上有。因此我们只需要做log次等差数列求交,就可以有多少个位置匹配。
现在的问题变为求 Lmax,Rmax L m a x , R m a x ,假设现在要求 Lmax L m a x ,那么我们建出 Si S i 的反串的后缀树,在后缀树上按 f>u f − > u 走到某个终止节点,这个终止节点上方最近的一个后缀出现的位置即为最长的后缀,反过来就是 Lmax L m a x

接下来考虑代替后缀树,原树上终止节点的位置满足 f>u f − > u 和某个后缀的 lcp l c p 最长,那么这个位置一定是字典序小于等于 f>u f − > u 的最后一个位置,我们用 SAM S A M parent p a r e n t 树上反着跳就可以求出这个位置。
接下来怎么找最近呢?后缀树上的两个后缀的父子关系满足父亲既是儿子的前缀,又是儿子的后缀,即对应这个后缀的 border b o r d e r ,可以用 hash+ h a s h + 倍增找到最近的点。
分析到这里发现实际上 SAM S A M 也是不需要的了,我们可以去掉这一步- -。

时间复杂度是 O((n+q)log2n) O ( ( n + q ) l o g 2 n ) 的,然后就是码码码不回头。

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;

typedef long long LL;
typedef pair<char,int> pci;
typedef pair<int,char> pic;
typedef pair<LL,int> pli;
typedef pair<int,int> pii;
typedef pair<int, pii > piii;
const LL INF=(LL)2e18;
const int inf=2e9,mod=1e9+7;
const int N=2e5+10,M=1800010;
const LL bas=233,hsmod=(LL)100000000000031;
const int hsmsk=(1<<22)-1;

LL qpow(LL x,LL y) {LL ret=1; for(;y;y>>=1,x=x*x%mod) if(y&1) ret=ret*x%mod; return ret;}

namespace KMP
{
    LL rdown(LL x,LL y) {return y<0?rdown(-x,-y):(x>0?x/y:-(-x+y-1)/y);}
    LL rup(LL x,LL y) {return -rdown(-x,y);}

    struct sub
    {
        int s,d,cnt;
        sub(int S=0,int D=0,int CNT=0) {s=S;d=D;cnt=CNT;}
        int end()const{return s+(cnt-1)*d;}
        bool have(int x)const{return x>=s && x<=end() && !((x-s)%d);}
        int calc(const sub &t)
        {
            if(d==t.d) 
            {
                if((s-t.s)%d) return 0;
                return max(0,(min(end(),t.end())-max(s,t.s))/d+1);
            }
            else if(cnt==1) return t.have(s);
            else return have(t.s);
        }
        sub flip(int x) {return sub(x-(s+(cnt-1)*d),d,cnt);}
    };

    vector<int> fail[N],slink[N];
    void construct(char s[],int n,int id)
    {
        vector<int> &f=fail[id],&g=slink[id];
        f.resize(n+1);g.resize(n+1);f[0]=f[1]=0;
        for(int i=2;i<=n;++i)
        {
            int &p=f[i];p=f[i-1];
            while(p && s[p+1]^s[i]) p=f[p];
            if(s[p+1]==s[i]) ++p;
        }
        for(int i=1;i<=n;++i) g[i]=f[i]==0?0:( ((i-f[i])==(f[i]-f[f[i]])) ?g[f[i]]:f[i]);
    }

    vector<sub> extract(int id,int x)
    {
        vector<int> &f=fail[id],&g=slink[id];
        assert(x<f.size() && x<g.size());
        vector<sub>ret;
        while(x)
        {
            int d=x-f[x];
            ret.pb(sub(g[x]+d,d,(x-g[x])/d)); x=g[x];
        }
        reverse(ret.begin(),ret.end());
        if(ret.back().cnt>1) ret.back().cnt--,ret.pb(sub(ret.back().end()+ret.back().d,1,1));
        return ret;
    }

    int query(int x,int xl,int y,int yl,int tot)
    {
        if(!xl || !yl || xl+yl<tot) return 0;
        vector<sub> vx=extract(x,xl),vy=extract(y,yl);
        for(int i=0;i<vy.size();++i) vy[i]=vy[i].flip(tot);
        reverse(vy.begin(),vy.end());
        int itx=0,ity=0,ret=0;
        while(itx<vx.size() && ity<vy.size())
        {
            ret+=vx[itx].calc(vy[ity]);
            if(vx[itx].end()<=vy[ity].end()) ++itx;
            else ++ity;
        }
        return ret;
    }
};

struct BIT
{
    int bit_table[M];
    int lowbit(int x) {return x&(-x);}
    void add(int x,int v) {for(;x<M;x+=lowbit(x)) bit_table[x]+=v;}
    int query(int x) {int ret=0;for(;x;x-=lowbit(x)) ret+=bit_table[x];return ret;}
}bit;

struct HashTable
{
    int hs[hsmsk],val[hsmsk];
    HashTable() {memset(hs,-1,sizeof(hs));memset(val,-1,sizeof(val));}
    int getpos(int p) {return ((p<<5)^(p>>3))&hsmsk;}
    int find(int c) {int p=getpos(c);while(~hs[p] && hs[p]^c) p=(p+1)&hsmsk; return val[p];}
    int& get(int c) 
    {
        int p=getpos(c); while(~hs[p] && hs[p]^c) p=(p+1)&hsmsk;
        if(!~hs[p]) hs[p]=c,val[p]=-1; return val[p];
    } 
};

namespace ACM
{
    int tot=1,fa[M],pc[M]; LL hs[M];
    pli hsarr[M]; vector<pci> go[M];
    HashTable mp;

    int insert(int p,int c)
    {
        int &q=mp.get(p<<8|c);
        if(q==-1)
        {
            q=++tot;fa[q]=p;pc[q]=c;hs[q]=(hs[p]*bas+c+1)%hsmod;
            go[p].pb(mkp(c,q));
            assert(tot<M);
        }
        return q;
    }   

    int ind,fail[M],idfn[M],idfnr[M];
    vector<int> con[M];

    void idfs(int x)
    {
        idfn[x]=++ind;
        for(int i=0;i<con[x].size();++i) idfs(con[x][i]);
        idfnr[x]=ind;
    }

    int son[M],top[M],dep[M];
    set<int> st[M];
    int dfs1(int x)
    {
        int sz=1,mx=0;
        for(int i=0;i<go[x].size();++i)
        {
            int v=go[x][i].se;dep[v]=dep[x]+1;
            int szv=dfs1(v);sz+=szv;
            if(szv>mx) son[x]=v,mx=szv;
        }
        return sz;
    }

    void dfs2(int x,int tp)
    {
        top[x]=tp; if(!son[x]) return;
        dfs2(son[x],tp);
        for(int i=0;i<go[x].size();++i)
        {
            int v=go[x][i].se;
            if(v==son[x]) continue;
            dfs2(v,v);  
        }   
    } 

    void construct() {dep[1]=0;dfs1(1);dfs2(1,1);}
    void flip(int x)
    {
        int g=top[x];
        if(st[g].find(dep[x])==st[g].end()) st[g].insert(dep[x]);
        else st[g].erase(dep[x]);
    }

    int query(int x)
    {
        while(x)
        {
            if(st[top[x]].size()>0 && *st[top[x]].begin()<=dep[x]) return *(--st[top[x]].lower_bound(dep[x]+1));
            x=fa[top[x]];
        }
        return 0;
    }

    int q[M],qn;
    void build()
    {
        construct();
        for(int i=1;i<=tot;++i) hsarr[i]=mkp(hs[i],i);
        sort(hsarr+1,hsarr+tot+1);qn=0;q[qn++]=1;
        for(int i=0;i<qn;++i)
        {
            int x=q[i];
            for(int j=0;j<go[x].size();++j) q[qn++]=go[x][j].se;
        }
        fail[1]=1;
        for(int i=1;i<qn;++i)
        {
            int x=q[i],&p=fail[x];
            if(fa[x]==1) p=1;
            else
            {
                p=fail[fa[x]];
                while(p^1 && !~mp.find(p<<8|pc[x])) p=fail[p];
                if(~mp.find(p<<8|pc[x])) p=mp.find(p<<8|pc[x]);
            }
            con[fail[x]].pb(x);
        }
        idfs(1);
    }

    int qans[M],qans2[M];
    vector<pii> qr[M],qr2[M];

    void dfs_solve1(int x)
    {
        bit.add(idfn[x],1);
        for(int i=0;i<qr[x].size();++i)
        {
            int pos=qr[x][i].fi;
            qans[qr[x][i].se]+=bit.query(idfnr[pos])-bit.query(idfn[pos]-1);
        }
        for(int i=0;i<go[x].size();++i) dfs_solve1(go[x][i].se);
        bit.add(idfn[x],-1);
    }

    void dfs_solve2(int x,LL hs=0,LL hsbs=1)
    {
        int p=lower_bound(hsarr+1,hsarr+tot+1,mkp(hs,0))-hsarr;
        if(hsarr[p].fi==hs) flip(hsarr[p].se);
        for(int i=0;i<qr2[x].size();++i) qans2[qr2[x][i].se]=query(qr2[x][i].fi);
        for(int i=0;i<go[x].size();++i) dfs_solve2(go[x][i].se,(hs+hsbs*(go[x][i].fi+1))%hsmod,hsbs*bas%hsmod);
        if(hsarr[p].fi==hs) flip(hsarr[p].se);
    }

    void add_query(int ui,int vi,int si,int ti,int id)
    {
        qr[ui].pb(mkp(ti,id));qr[vi].pb(mkp(si,id));
        qr2[si].pb(mkp(ui,id<<1));qr2[ti].pb(mkp(vi,id<<1|1));
    }

    piii get_query(int id) {return mkp(qans[id],mkp(qans2[id<<1],qans2[id<<1|1]));}

    void solve()
    {
        memset(qans,0,sizeof(qans));
        dfs_solve1(1);dfs_solve2(1);
    }
};

namespace solution
{
    int n,m,q,cur_rt,cur_dp,an;
    int qu[N],qv[N],qx[N],uid[N],vid[N];
    int dp[N],siz[N],arr[N],gr[N],id[N];
    char s[N];
    vector<pic>con[N]; vector<pii>qr[N]; vector<int> sid[N];

    void pdfs(int x,int f=-1)
    {
        siz[x]=1;arr[an++]=x;dp[x]=cur_dp+1;
        for(int i=0;i<con[x].size();++i)
        {
            int v=con[x][i].fi;
            if(v==f || dp[v]<cur_dp) continue;
            pdfs(v,x);siz[x]+=siz[v];
        }
    }

    int findroot(int x)
    {
        an=0;pdfs(x); int ret=x;
        for(int i=0;i<an;++i) if(siz[arr[i]]*2>=siz[x] && siz[arr[i]]<siz[ret]) ret=arr[i];
        return ret;
    }

    void dfs(int x,int f,int g)
    {
        gr[x]=g;
        for(int i=0;i<con[x].size();++i)
        {
            int v=con[x][i].fi;
            if(v==f || dp[v]<cur_dp) continue;
            id[v]=ACM::insert(id[x],con[x][i].se-'a');
            dfs(v,x,f==-1?v:g);
        }
    }

    void solve(int X,int d)
    {
        cur_dp=d;int rt=findroot(X);cur_rt=rt;
        dp[rt]=d;id[rt]=1;dfs(rt,-1,rt);
        static bool vis[N];
        for(int i=0;i<an;++i) vis[arr[i]]=1;
        for(int i=0;i<an;++i)
        {
            int x=arr[i];
            for(int j=0;j<qr[x].size();++j)
            {
                int y=qr[x][j].fi;
                if(vis[y] && gr[y]^gr[x]) uid[qr[x][j].se]=id[x],vid[qr[x][j].se]=id[y];
            }
        }
        for(int i=0;i<an;++i) vis[arr[i]]=0;
        for(int i=0;i<con[rt].size();++i)
        {
            int v=con[rt][i].fi;
            if(dp[v]<d) continue;
            solve(v,d+1);
        }
    }

    void end_of_the_world()
    {
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<n;++i)
        {
            int u,v;char c[2]; scanf("%d%d%s",&u,&v,c);
            con[u].pb(mkp(v,c[0]));con[v].pb(mkp(u,c[0]));
        }
        for(int i=1;i<=m;++i)
        {
            scanf("%s",s+1);int l=strlen(s+1),p=1; sid[i].pb(1);
            for(int j=1;j<=l;++j) p=ACM::insert(p,s[j]-'a'),sid[i].pb(p);
            KMP::construct(s,l,i); 
            reverse(s+1,s+l+1);p=1;sid[i+m].pb(1);
            for(int j=1;j<=l;++j) p=ACM::insert(p,s[j]-'a'),sid[i+m].pb(p);
            KMP::construct(s,l,i+m);
        }
        for(int i=1;i<=q;++i)
        {
            int u,v,x;scanf("%d%d%d",&u,&v,&x);
            qr[u].pb(mkp(v,i));qu[i]=u;qv[i]=v;qx[i]=x;
        }
        solve(1,0);
        ACM::build();
        for(int i=1;i<=q;++i) ACM::add_query(uid[i],vid[i],sid[qx[i]].back(),sid[qx[i]+m].back(),i);
        ACM::solve();
        for(int i=1;i<=q;++i) piii pp=ACM::get_query(i);
        for(int i=1;i<=q;++i)
        {
            piii pp=ACM::get_query(i);int ans=pp.fi;
            ans+=KMP::query(qx[i],pp.se.fi,qx[i]+m,pp.se.se,sid[qx[i]].size()-1);
            printf("%d\n",ans);
        }
    }
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("CF917E.in","r",stdin);
    freopen("CF917E.out","w",stdout);
#endif
    solution::end_of_the_world();

    return 0;
}

【总结】
还行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值