【学习笔记】「JOISC 2022 Day2」复制粘贴 3

看了正解。我觉得很厉害。虽然用减枝水过去了。

区间 d p dp dp。但是这个转移怎么看都不是 O ( 1 ) O(1) O(1)的。

border \text{border} border 那么 trick \text{trick} trick应该都能看出来。能进行剪切操作当且仅当 s [ l , p ] = s [ q , r ] s_{[l,p]}=s_{[q,r]} s[l,p]=s[q,r],显然直接跳 fail \text{fail} fail链即可。厉害的地方来了,对于两个相同的子串只用计算一次,而每跳一次至少会出现一对相同的子串,因此总转移数目只有 O ( n 2 ) O(n^2) O(n2)

问题在于求出区间 [ l , r ] [l,r] [l,r]内最多能选多少个不重复的 s [ l , p ] s_{[l,p]} s[l,p]。更厉害的地方来了,这个东西可以倍增预处理,设 g l , r , k g_{l,r,k} gl,r,k表示和 s [ l , r ] s_{[l,r]} s[l,r]相同的不重叠的第 2 k 2^k 2k个串的左端点,然后就做完了。

复杂度是严格的 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)当然减一减枝也能过。

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define inf 0x3f3f3f3f3f3f3f3f using namespace std; int n,nxt[2505][2505],to[2505][2505]; int trie[2505*2505][26],g[2505][2505][12],len[2505*2505],tot; ll dp[2505][2505],A,B,C; vector<int>pos[2505*2505]; string s; void chmin(ll &x,ll y){x=min(x,y);} int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>s;memset(dp,0x3f,sizeof dp); cin>>A>>B>>C; //fixed for(int i=0;i<n;i++){ nxt[i][i]=i-1; for(int j=i+1;j<n;j++){ int k=nxt[i][j-1]; while(k>=i&&s[k+1]!=s[j])k=nxt[i][k]; if(s[k+1]==s[j])k++; nxt[i][j]=k; } } for(int i=0;i<n;i++){ int it=0; for(int j=i;j<n;j++){ if(!trie[it][s[j]-'a'])trie[it][s[j]-'a']=++tot,len[tot]=j-i+1; it=trie[it][s[j]-'a']; pos[it].pb(i); to[i][j]=it; } } for(int i=1;i<=tot;i++){ sort(pos[i].begin(),pos[i].end()); int k=0; for(int j=0;j<pos[i].size();j++){ while(k<pos[i].size()&&pos[i][k]-pos[i][j]<len[i])k++; if(k!=pos[i].size()){ g[pos[i][j]][len[i]][0]=pos[i][k]; } } } for(int k=1;k<=11;k++){ for(int i=0;i<n;i++){ for(int j=1;j<=n-i;j++){ if(g[i][j][k-1])g[i][j][k]=g[g[i][j][k-1]][j][k-1]; } } } for(int i=0;i<n;i++)dp[i][i]=A; for(int len=2;len<=n;len++){ for(int i=0;i<n-len+1;i++){ int j=i+len-1; if(pos[to[i][j]][0]!=i){ dp[i][j]=dp[pos[to[i][j]][0]][pos[to[i][j]][0]+len-1]; continue; } chmin(dp[i][j],dp[i+1][j]+A); chmin(dp[i][j],dp[i][j-1]+A); //fixed for(int k=nxt[i][j];k>=i;k=nxt[i][k]){ int tot=1,nowl=i,len2=k-i+1; for(int l=11;l>=0;l--){ if(g[nowl][len2][l]&&g[nowl][len2][l]<=j-len2+1){ tot+=1<<l; nowl=g[nowl][len2][l]; } } chmin(dp[i][j],dp[i][k]+B+tot*C+(len-tot*len2)*A); } } } cout<<dp[0][n-1]; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529981.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(33)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示