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. uni-app开发注意事项

    关于vue 1.注意 如果使用老版的非自定义组件模式,即manifest中"usingComponents":false,部分模版语法不支持,但此模式已于2019年11月起下线. ...

  2. 手把手教你在容器服务 TKE 上使用 LB 直通 Pod

    什么是 LB 直通 Pod ? Kubernetes 官方提供了 NodePort 类型的 Service,即给所有节点开一个相同端口用于暴露这个 Service,大多云上 LoadBalancer ...

  3. 学会Git玩转GitHub(第一篇) 入门详解 - 精简归纳

    学会Git玩转GitHub(第一篇) 入门详解 - 精简归纳 JERRY_Z. ~ 2020 / 9 / 25 转载请注明出处!️ 目录 学会Git玩转GitHub(第一篇) 入门详解 - 精简归纳 ...

  4. 对于filter的理解

    filter语法:使用filter会创建一个新数组,所以原数组不变 array.filter(function(value,index,arr), thisValue) 其中:arr:数组(可选) i ...

  5. kubernetes1.15极速部署prometheus和grafana

    关于prometheus和grafana prometheus负责监控数据采集,grafana负责展示,下图来自官网: 环境信息 硬件:三台CentOS 7.7服务器 kubernetes:1.15 ...

  6. 晚间测试3 B. 单(single)

    题目描述 单车联通大街小巷.这就是出题人没有写题目背景的原因. 对于一棵树,认为每条边长度为 \(1\),每个点有一个权值\(a[i]\).\(dis(u,v)\)为点\(u\)到\(v\)的最短路径 ...

  7. 使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源(二)

    之前写过一篇 使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源,在那之后,又陆续想到并实施了几点利用 jsDelivr 进一步加速静态资源加载的措施,新起一篇作为记录和分享. ...

  8. 跟我一起学Redis之五种基本类型及其应用场景举例(干了6个小时)

    前言 来啦,老弟?来啦,上一篇就当唠唠嗑,接下来就开始进行实操撸命令,计划是先整体单纯说说Redis的各种用法和应用,最后再结合代码归纳总结. Redis默认有16个数据库(编号为0~15),默认使用 ...

  9. 用于ASP.net的MVC模块

    下载MVCBricks_SRC - 492.58 KB 表的内容 介绍系统要求游戏闪屏的最终考虑历史 介绍 自从我写上一篇关于ASP的文章以来,已经有很长时间了.净的话题.这次我决定写一些关于它的东西 ...

  10. ansible-命令使用说明

    1. ansible命令的使用说明 ansible 主机或组-m 模块名-a '模块参数' ansible参数 表示调用什么模块,使用模块的那些参数 • 主机和组,是在/etc/ansible/hos ...