Wannafly挑战赛29 F 最后之作

Wannafly挑战赛29 F 最后之作

https://ac.nowcoder.com/acm/contest/271/F

分析:

  • 首先有\(O(k^2)\)的暴力\(dp\), 然后可以发现当\(g\)固定的时候转移是一个类似\(kx+b\)的形式,且\(k\ge 0\)
  • 有一种比较好想的\(O(nklogn^2)\)做法,这里就不说了,可能影响对正解的理解。
  • 枚举\(r\),考虑所有的\(g(l,r)\),可以发现这些\(g\)递减且不超过\(n\),于是我们用trie树的简单trick可以得到\(g(l,r)=i\)的分界线。
  • 然后我们维护\(n\)棵李超线段树,分别存\(g(l,r)=i\)\(l\)的贡献,这里由于\(k\ge 0\),左端点移出范围时不需要删除,因为答案不会更优,同时只剩下右端点,这个随着\(r\)增大也是递增的
  • 最多有\(nk\)条直线需要插入,总时间复杂度\(O(nklogn)\)

代码:

//❤ ayaponzu*
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;
typedef long long ll;
#define N 1000050
vector<int>w[N],pos[N];
int n,K,a[N],b[N],P,now[N],root[N];
char S[N];
int ch[N][26],cnt=1,dep[N],siz[N];
ll f[N];
void insert(int s) {
    int i,p=1;
    for(i=1;i<=K;i++) {
        int x=w[s][i];
        if(!ch[p][x]) {
            ch[p][x]=++cnt; dep[ch[p][x]]=i; siz[i]++;
        } p=ch[p][x];
    }
}
int merge(int x,int y) {
    if(!x||!y) return x+y;
    siz[dep[y]]--;
    int i;
    for(i=0;i<26;i++) ch[x][i]=merge(ch[x][i],ch[y][i]);
    return x;
}
void work(int x) {
    int i;
    for(i=1;i<=n;i++) {
        int l=1,r=x+1;
        while(l<r) {
            int mid=(l+r)>>1;
            if(siz[K-mid+1]>=i)l=mid+1;
            else r=mid;
        }l--;
        pos[x][i]=l;
    }
}
int tot,ls[N<<1],rs[N<<1];
ll kk[N<<1],bb[N<<1];
void build(int l,int r,int &p) {
    p=++tot; bb[p]=-1ll<<60;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(l,mid,ls[p]); build(mid+1,r,rs[p]);
}
void update(int l,int r,ll K,ll B,int p) {
    ll vl1=kk[p]*l+bb[p], vr1=kk[p]*r+bb[p];
    ll vl2=K*l+B, vr2=K*r+B;
    if(vl2>=vl1&&vr2>=vr1) {kk[p]=K,bb[p]=B;return ;}
    if(vl2<=vl1&&vr2<=vr1) return ;
    int mid=(l+r)>>1;
    if(K*mid+B>kk[p]*mid+bb[p]) swap(K,kk[p]),swap(B,bb[p]);
    update(l,mid,K,B,ls[p]); update(mid+1,r,K,B,rs[p]);
}
ll query(int l,int r,int x,int p) {
    if(l==r) return kk[p]*x+bb[p];
    int mid=(l+r)>>1;ll re=kk[p]*x+bb[p];
    if(x<=mid) return max(re,query(l,mid,x,ls[p]));
    else return max(re,query(mid+1,r,x,rs[p]));
}
int main() {
    scanf("%d%d%d",&n,&K,&P);
    int i,j;
    for(i=1;i<=K;i++) scanf("%d",&a[i]),a[i]=-a[i];
    for(i=1;i<=K;i++) scanf("%d",&b[i]),b[i]=-b[i];
    for(i=1;i<=n;i++) {
        scanf("%s",S+1);
        w[i].resize(K+1);
        for(j=1;j<=K;j++) w[i][K-j+1]=S[j]-'a';
    }
    int rt=1;
    for(i=1;i<=n;i++) insert(i);
    for(i=1;i<=K;i++) pos[i].resize(n+1);
    work(K);
    for(i=K-1;i;i--) {
        int x=ch[rt][0];
        for(j=1;j<26;j++) x=merge(x,ch[rt][j]);
        rt=x;
        work(i);
    }
 
    /*for(i=1;i<=K;i++) {
        for(j=1;j<=n;j++) printf("%d ",pos[i][j]);
        puts("");
    }*/
 
    for(i=1;i<=n;i++) build(1,K,root[i]);
    for(i=0;i<=K;i++) f[i]=-1ll<<60;
    f[0]=0;
    for(i=1;i<=K;i++) {
        for(j=1;j<=n;j++) {
            int x=pos[i][j];
            while(now[j]<x) {
                now[j]++;
                int u=now[j];
                update(1,K,a[u],f[u-1]+b[u]+a[u]*(1-u),root[j]);
            }
            f[i]=max(f[i],P*j+query(1,K,i,root[j]));
        }
    }
    printf("%lld\n",f[K]);
 
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/suika/p/10970822.html