[HDU4117] GRE

发布时间 2023-09-09 17:48:32作者: 灰鲭鲨

Recently George is preparing for the Graduate Record Examinations (GRE for short). Obviously the most important thing is reciting the words.
Now George is working on a word list containing \(N\) words.
He has so poor a memory that it is too hard for him to remember all of the words on the list. But he does find a way to help him to remember. He finds that if a sequence of words has a property that for all pairs of neighboring words, the previous one is a substring of the next one, then the sequence of words is easy to remember.
So he decides to eliminate some words from the word list first to make the list easier for him. Meantime, he doesn't want to miss the important words. He gives each word an importance, which is represented by an integer ranging from \(-1000\) to \(1000\), then he wants to know which words to eliminate to maximize the sum of the importance of remaining words. Negative importance just means that George thought it useless and is a waste of time to recite the word.
Note that although he can eliminate any number of words from the word list, he can never change the order between words. In another word, the order of words appeared on the word list is consistent with the order in the input. In addition, a word may have different meanings, so it can appear on the list more than once, and it may have different importance in each occurrence.

空间限制:32MB
时间限制:15s

考虑倒着来,建出 AC 自动机,考虑倒着来,修改后变成一个询问子树最大值,用线段树维护就可以了。
但是空间32MB?

26<32,把一个字符拆成 5 个01,比如把 'a' 拆成 00000,'b' 拆成 00001,然后空间就变成了 \(5\times 2\times N\)

当然也可以三个 \(3\times 10^5\) 压成 1 个 long long。

线段树也是可以压的,注意到关键点只有 \(2\times 10^4\) 个,所以可以找到离一个点最近的关键点祖先。

能共用的数组就共用,dfs写手写栈。

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,M=2e4+5;
bool x;
int n,t,m,p[M],e_num,tme,q[N],idx,fil[N],l,r,ans,hd[N],ln[M],fr[N],id[M],w[M],sz[N];
bool tg[N];
long long tr[N][9];
char str[N];
struct edge{
    int v,nxt;
}e[N];
bool y;
int qry(int u,int c)
{
    if(c%3==0)
        return tr[u][c/3]&1048575;
    if(c%3==1)
        return (tr[u][c/3]>>20)&1048575;
    return tr[u][c/3]>>40;
}
void gai(int u,int c,int t)
{
    if(c%3==0)
        tr[u][c/3]=tr[u][c/3]&1152921504605798400LL|t;
    if(c%3==1)
        tr[u][c/3]=tr[u][c/3]&1152920405096267775LL|((1LL*t)<<20);
    if(c%3==2)
        tr[u][c/3]=tr[u][c/3]&1099511627775LL|((1LL*t)<<40LL);
}
void add_edge(int u,int v)
{
    e[++e_num]=(edge){v,hd[u]};
    hd[u]=e_num;
}
int insert(char s[])
{
    int u=0;
    for(int i=0,k;s[i];i++)
    {
        if(!(k=qry(u,s[i]-'a')))
        {
            gai(u,s[i]-'a',k=++idx);
            memset(tr[idx],0,sizeof(tr[idx]));
        }
        u=k;
    }
    return u;
}
int cmp(int x,int y)
{
    return ln[x]-ln[x-1]>ln[y]-ln[y-1];
}
void upd(int o,int l,int r,int x,int y)
{
    if(l==r)
        return hd[o]=max(hd[o],y),void();
    int md=l+r>>1;
    if(md>=x)
        upd(o<<1,l,md,x,y);
    else
        upd(o<<1|1,md+1,r,x,y);
    hd[o]=max(hd[o<<1],hd[o<<1|1]);
}
int ask(int o,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)
        return hd[o];
    int md=l+r>>1,ans=0;
    if(md>=x)
        ans=max(ans,ask(o<<1,l,md,x,y));
    if(md<y)
        ans=max(ans,ask(o<<1|1,md+1,r,x,y));
    return ans;
}
void build()
{
    memset(fil,0,sizeof(fil));
    l=1,r=0;
    for(int i=0,k;i<26;i++)
        if(k=qry(0,i))
            q[++r]=k;
    while(l<=r)
    {
        for(int i=0,k;i<26;i++)
        {
            if(k=qry(q[l],i))
                fil[q[++r]=k]=qry(fil[q[l]],i);
            else
                gai(q[l],i,qry(fil[q[l]],i));
        }
        ++l;
    }
    for(int i=1;i<=idx;i++)
        add_edge(fil[i],i);
    memset(tg,0,sizeof(tg));
    memset(sz,0,sizeof(sz));
    for(int i=1;i<=n;i++)
        tg[p[i]]=sz[p[i]]=1;
    for(int i=1;i<=r;i++)
    {
        if(sz[q[i]])
            fr[q[i]]=q[i];
        else
            fr[q[i]]=fr[fil[q[i]]];
    }
    for(int i=r;i;--i)
        sz[fil[q[i]]]+=sz[q[i]];
    q[l=r=1]=0;
    while(r)
    {
        int x=q[r];
        --r;
        if(tg[x])
            fil[x]=++tme;
        for(int i=hd[x];i;i=e[i].nxt)
            q[++r]=e[i].v;
    }
}
int main()
{
    //printf("%d\n",(&y-&x)>>20);
    scanf("%d",&t);
    for(int T=1;T<=t;T++)
    {
        memset(tr[0],0,sizeof(tr[0]));
        memset(hd,tme=idx=e_num=0,sizeof(hd));
        ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s%d",str+ln[i-1],w+i);
            p[i]=insert(str+ln[i-1]);
            ln[i]=ln[i-1]+strlen(str+ln[i-1]);
        }
        build();
        memset(hd,0,sizeof(hd));
        for(int i=n,dp;i;i--)
        {
            int u=0;
            ans=max(ans,dp=w[i]+max(0,ask(1,1,tme,fil[p[i]],fil[p[i]]+sz[p[i]]-1)));
            for(int j=ln[i-1];j<ln[i];j++)
            {
                u=qry(u,str[j]-'a');
                if(fil[fr[u]])
                    upd(1,1,tme,fil[fr[u]],dp);
            }
        }
        printf("Case #%d: %d\n",T,ans);
    }
}