Codeforce 582 B. Once Again... 解析(思維、DP、LIS、矩陣冪)

今天我們來看看CF582B

題目連結

題目

給你一個長度為\(n\)的數列\(a\),求\(a\)循環\(T\)次以後的最大遞增子序列(LIS)。\(n\le100,T\le10^7\)

前言

這題實在是搞了非常非常久,經驗過於不足,無論是矩陣快速冪的奇怪\(dp\)式或者是偏思維作法,都搞不太出來

想法

首先,要來講一下\(LIS\)的作法:

  1. \(dp:\) 我們維護以第\(i\)個元素為結尾的\(LIS\)長度為\(dp[i]\),轉移式\(:dp[i]=\max\limits_{j=0,a[j]\le a[i]}^n\{dp[i],dp[j]+1\}\) 且要考慮\(LIS\)只有單一個元素時長度為1,所以\(dp[i]\)有個最小值\(1\)。

    複雜度:\(O(n^2)\)
  2. 二分搜\(:\) 我們維護一個\(stack\),且每次加入一個元素考慮\(LIS\)。\(stack\)頂端放目前維護的\(LIS\)的最後一個元素,如果新元素\(\ge\)頂端的元素,那就加入。如果新元素\(<\)頂端的元素,那麼我們可以二分搜目前維護的\(LIS\)中第一個大於新元素的元素,並且替換為新元素,如此一來,因為我們換了一個比較小的元素上去,且不影響目前的\(LIS\)長度,因此整個\(stack\)在之後能獲得更好的遞增子序列的「潛力」就變高了。非常建議直接看code思考為什麼這樣做

    複雜度:\(O(n\lg(n))\)
int LIS(){
int sta[_n*_n],top=0;
sta[top++]=a[0];
rep(i,1,nn){
if(a[i]>=sta[top-1])sta[top++]=a[i];
else *upper_bound(sta,sta+top,a[i])=a[i];
}
return top;
}

回歸正題,這題有兩種做法,偏思維作法或者是奇怪\(dp\)式做法。

  1. 偏思維作法

    畢竟循環(\(T\))太長了,而\(n\)又很小,所以自然地感覺應該會用到經典的\(LIS\)(最大遞增子序列)問題再加上一些觀察。

    注意到,最慢最慢在數列\(a\)循環\(n\)次以後,\(LIS\)會重複某個字元。也就是如果每次循環我們都取一個相異的元素,那麼\(n\)次循環以後就不可能再取到相異的元素了。而因此,我們會想要去維護長度為\(n^2\)的\(LIS\)(如果\(T>n\))。

    現在,如果\(T\le n\),那麼我們只要暴力算出\(LIS\)就行,畢竟\(n\)很小。

    如果\(T>n\),觀察到,因為\(T>n\),所以我們可以想像得出,整串\(LIS\)一定有非常多重複的數字,整串\(LIS\)會先遞增,然後到某個數字開始重複,接著在尾端繼續遞增。

    那麼我們只要先算出數列\(a\)中最多重複出現幾次同樣的數字,並且算出長度\(n^2\)的\(LIS\)以後,由於我們知道當\(T>n\)時,至少會有\(T-n\)次\(a\)數列的循環會貢獻給\(LIS\)同樣的數字,因此,解答就是:\(LIS(長度n^2的)+(T-n)\times(出現次數最多的數字)\)。(想法來源)

    還有另一個可能會想到的作法,但是複雜度會高一些,直接放連結

  2. 奇怪\(dp\)式做法:

    其實這個做法是矩陣快速冪,但我不知道為什麼想得到這種狀態

    考慮\(dp\)狀態: \(dp[i][j]\)表示「目前為止考慮過的循環節」(考慮過幾個循環節並沒有在狀態中)的,開頭元素\(\ge a[i]\)且結尾為最後一個循環的第\(j\)個元素的\(LIS\)長度。

    而會有一個轉移式\(:dp_{p+q}[i][j]=\max\limits_{k=0}^n\{dp_p[i][k]+dp_q[k][j]\}\)(就是把兩個對於(不/相)同長度循環節的\(LIS\)接起來)

    其中\(dp_p\)表示目前這個\(dp\)狀態考慮的數列長度是\(p\times n\)(也就是\(p\)個循環節)

    我們想要求的是\(dp_{nT}\),而我們可以用樸素的\(dp\)算法(\(O(n^2)\)的方法)來算出\(dp_1\)。

    把\(dp[][]\)看成是矩陣,而一次轉移看成是一次矩陣乘法,那麼就可以用矩陣快速冪算出來了。

    要注意如果在算\(dp[i][j]\)時如果\(a[i]>a[j]\)代表這種可能不可能發生,要給一個極小值。

複雜度:偏思維作法中較慢的\(:O(n^4)\)、偏思維作法中較快的想法但是沒用二分搜\(LIS:O(n^4)\)、矩陣快速冪\(:O(n^3\lg(T))\)、偏思維作法中較快的想法且有用二分搜\(:O(n^2\lg(n^2))\)

程式碼(偏思維作法中較慢的):

const int _n=110;
int tt,a[_n*_n],st[_n*_n],ed[_n*_n],num[310];
ll n,nn,t;
main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>t;rep(i,0,n){cin>>a[i];num[a[i]]++;} nn=min(n*n,n*t);
rep(i,n,nn)a[i]=a[i%n];
rep(i,0,nn){ed[i]=1;{rep(j,0,i)if(a[j]<=a[i])ed[i]=max(ed[i],ed[j]+1);}}
per(i,0,nn){st[i]=1;{per(j,i+1,nn)if(a[j]>=a[i])st[i]=max(st[i],st[j]+1);}}
int maxx=-1;
if(t<n)rep(i,0,nn)maxx=max(maxx,ed[i]);
else rep(i,0,nn)maxx=max(maxx,ed[i]+st[i]-1+num[a[i]]*(t-n));
cout<<maxx<<'\n';
return 0;
}

標頭、模板請點Submission看

Submission

程式碼(偏思維作法中較快的想法但是沒用二分搜):

const int _n=110;
int tt,a[_n*_n],dp[_n*_n],num[310];
ll n,nn,t,k;
main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>t;rep(i,0,n){cin>>a[i];num[a[i]]++;k=max(k,num[a[i]]);}
nn=min(n*n,n*t);
rep(i,n,nn)a[i]=a[i%n]; dp[0]=1;
rep(i,1,nn){
dp[i]=1;
rep(j,0,i)if(a[j]<=a[i])dp[i]=max(dp[i],dp[j]+1);
}
if(t>n){
int ans=k*(t-n);
int maxx=0;rep(i,0,nn)maxx=max(maxx,dp[i]);
ans+=maxx;
cout<<ans<<'\n';
}else{
int maxx=0;rep(i,0,nn)maxx=max(maxx,dp[i]);
cout<<maxx<<'\n';
}
return 0;
}

標頭、模板請點Submission看

Submission

程式碼(矩陣快速冪):

const int _n=110;
int t,n,a[_n];
struct mat{
int a[_n][_n];
mat(){memset(a,0,sizeof a);}
mat operator*(const mat& rhs)const{
mat res;
rep(i,0,n)rep(j,0,n){
res.a[i][j]=-1e5;
rep(k,0,n)res.a[i][j]=max(res.a[i][j],a[i][k]+rhs.a[k][j]);
}
return res;
}
mat operator^(int b){
mat res,tmp=*this;
while(b){
if(b&1)res=res*tmp;
b>>=1;tmp=tmp*tmp;
}
return res;
}
};
main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>t;rep(i,0,n)cin>>a[i]; mat ans;
rep(i,0,n)rep(j,0,n){
if(a[i]>a[j]){ans.a[i][j]=-1e5;continue;}
ans.a[i][j]=1;
rep(k,0,j)if(a[k]<=a[j])ans.a[i][j]=max(ans.a[i][j],ans.a[i][k]+1);
}ans=ans^t;
int maxx=0;rep(i,0,n)rep(j,0,n)maxx=max(maxx,ans.a[i][j]);
cout<<maxx<<'\n';
return 0;
}

標頭、模板請點Submission看

Submission

程式碼(偏思維作法中較快的想法且有用二分搜):

const int _n=110;
int tt,a[_n*_n],num[310];
ll n,nn,t,k;
int LIS(){
int sta[_n*_n],top=0;
sta[top++]=a[0];
rep(i,1,nn){
if(a[i]>=sta[top-1])sta[top++]=a[i];
else *upper_bound(sta,sta+top,a[i])=a[i];
}
return top;
}
main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>t;rep(i,0,n){cin>>a[i];num[a[i]]++;k=max(k,num[a[i]]);}
nn=min(n*n,n*t);
rep(i,n,nn)a[i]=a[i%n];
if(t>n){
int ans=k*(t-n);
ans+=LIS();
cout<<ans<<'\n';
}else cout<<LIS()<<'\n';
return 0;
}

標頭、模板請點Submission看

Submission

B. Once Again... 解析(思維、DP、LIS、矩陣冪)的更多相关文章

  1. B. Kay and Snowflake 解析(思維、DFS、DP、重心)

    Codeforce 685 B. Kay and Snowflake 解析(思維.DFS.DP.重心) 今天我們來看看CF685B 題目連結 題目 給你一棵樹,要求你求出每棵子樹的重心. 前言 完全不 ...

  2. D. Alyona and Strings 解析(思維、DP)

    Codeforce 682 D. Alyona and Strings 解析(思維.DP) 今天我們來看看CF682D 題目連結 題目 略,請直接看原題. 前言 a @copyright petjel ...

  3. C. Vladik and Memorable Trip 解析(思維、DP)

    Codeforce 811 C. Vladik and Memorable Trip 解析(思維.DP) 今天我們來看看CF811C 題目連結 題目 給你一個數列,一個區段的數列的值是區段內所有相異數 ...

  4. D. New Year Santa Network 解析(思維、DFS、組合、樹狀DP)

    Codeforce 500 D. New Year Santa Network 解析(思維.DFS.組合.樹狀DP) 今天我們來看看CF500D 題目連結 題目 給你一棵有邊權的樹,求現在隨機取\(3 ...

  5. B. Nauuo and Circle 解析(思維、DP)

    Codeforce 1172 B. Nauuo and Circle 解析(思維.DP) 今天我們來看看CF1172B 題目連結 題目 略,請直接看原題 前言 第一個該觀察的事情一直想不到,看了解答也 ...

  6. D. Maximum Distributed Tree 解析(思維、DFS、組合、貪心、DP)

    Codeforce 1401 D. Maximum Distributed Tree 解析(思維.DFS.組合.貪心.DP) 今天我們來看看CF1401D 題目連結 題目 直接看原題比較清楚,略. 前 ...

  7. A. Arena of Greed 解析(思維)

    Codeforce 1425 A. Arena of Greed 解析(思維) 今天我們來看看CF1425A 題目連結 題目 略,請直接看原題. 前言 明明是難度1400的題目,但總感覺不是很好寫阿, ...

  8. E. Almost Regular Bracket Sequence 解析(思維)

    Codeforce 1095 E. Almost Regular Bracket Sequence 解析(思維) 今天我們來看看CF1095E 題目連結 題目 給你一個括號序列,求有幾個字元改括號方向 ...

  9. C2. Power Transmission (Hard Edition) 解析(思維、幾何)

    Codeforce 1163 C2. Power Transmission (Hard Edition) 解析(思維.幾何) 今天我們來看看CF1163C2 題目連結 題目 給一堆點,每兩個點會造成一 ...

随机推荐

  1. python中闭包详解

    谈谈自己的理解:python中闭包,闭包的实质   闭包这个概念好难理解,身边朋友们好多都稀里糊涂的,稀里糊涂的林老冷希望写下这篇文章能够对稀里糊涂的伙伴们有一些帮助~ 请大家跟我理解一下,如果在一个 ...

  2. Urule开源版系列5——RuleSetParser解析过程

    接上期Urule开源版系列4--Core包核心接口之规则解析过程 之前源码到了Parser,这期详细解析下RuleSetParser的解析过程 1.主流程 特殊处理一个属性 循环处理元素 当元素名称是 ...

  3. 7种jvm垃圾回收器,这次全部搞懂

    前言 之前我们讲解了jvm的组成结构与垃圾回收算法等知识点,今天我们来讲讲jvm最重要的堆内存是如何使用垃圾回收器进行垃圾回收,并且如何使用命令去配置使用这些垃圾回收器. 堆内存详解 上面这个图大家应 ...

  4. 关于消息中间件ActiveMQ的企业级应用

    几个月前看到项目中配置了activeMq,于是想通透的掌握activeMq,便去网上学习搜寻资料,找到这一篇博客挺不错的,解释的比较清楚,包括原理使用和配置,特此把它分享给大家. 众所周知,消息中间件 ...

  5. 【CF1425B】 Blue and Red of Our Faculty! 题解

    原题链接 简要翻译: 有一个连通图,A和B同时从点1出发,沿不同的路径前进.原本,图上的每一条边都是灰色的.A将经过的边涂成红色,B将经过的边涂成蓝色的.每个回合每个人只能走灰色的边.当某个回合中不存 ...

  6. Python练习题 037:Project Euler 009:毕达哥拉斯三元组之乘积

    本题来自 Project Euler 第9题:https://projecteuler.net/problem=9 # Project Euler: Problem 9: Special Pythag ...

  7. 解决Use 'LimitInternalRecursion' to increase the limit if necessary的问题 CodeIgniter .htaccess

    配置.htaccess如下: RewriteEngine on RewriteBase / RewriteCond $1 !^(index\.php|images|robots\.txt|css|js ...

  8. 怀疑安装MySQL之后,导致OrCAD Capture、Allegro就打不开

    记得在异常出现之前,只安装了MySQL,之后OrCAD Capture.Allegro就打不开了. Capture.exe - 系统错误 allegro.exe - 系统错误 我尝试在Cadence的 ...

  9. apline无法向gitlab上传git lfs问题

    1 背景 在k8s中基于alpine做底层系统的容器进行git lfs push操作时,发现报错无法上传成功 Fatal error: Server error: http://git.ops.xxx ...

  10. 2-K8S常用命令

    kubectl 命令行管理工具 类型 命令 描述 基础命令 create 通过文件名或标准输入创建资源 expose 为Deployment,Pod创建service run 在集群中运行一个特定的镜 ...