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. Redis5设计与源码分析读后感(一)认识Redis

    一.初识redis 定义 Redis是一个开源的Key-Value数据库,通常被称为数据结构服务器,其值可以是多种常见的数据格式,且读写性能极高,且所有操作都是原子性的. 高性能的主要原因 1.基于内 ...

  2. Redis学习(三)java使用redis

    一.操作步骤 Redis除了命令行操作以外,还可以通过java代码进行操作,流程如下: 下载Jedis依赖包,并丢入工程中合适的位置 在Maven中引入redis的包 <!--引入redis包- ...

  3. 图片压缩工具pngquant

    关于图片压缩的,之前看到一个imageOptim,用着不错,也挺好用的,直接打开要压缩的图片或者文件夹,唰唰唰的就开始压缩了,如下图 后来觉得不是很方面,还要打开软件,选择文件夹,然后就又研究了一下, ...

  4. Java知识系统回顾整理01基础06数组04增强型for循环

    增强型for循环在遍历一个数组的时候会更加快捷 一.增强型for循环 注:增强型for循环只能用来取值,却不能用来修改数组里的值 public class HelloWorld { public st ...

  5. C++(VS2015)模板显式特化之template语法深入理解

    首先说下遇到的情况: 这里在vc++6.0上建立了一个自定义模板类,再去覆盖这个类,分别使用部分覆盖,整体覆盖 但在vs2015上去整体覆盖类会报错. 错误如下: 错误原因:个人感觉是新版本的vs更接 ...

  6. vue+elementUI实现 分页表格的单选或者多选、及禁止部分选择

    一.vue+elementUI实现 分页表格前的多选 多选效果图: 代码如下: <el-table ref="multipleTable" :data="listD ...

  7. 启动VNC Shell扩展

    下载source files - 18.3 Kb Introduction 我们使用RealVNC来远程控制我们的网络中的pc机,VNC是一个伟大的产品,但如果不记住计算机名称,它可以是乏味的,在网络 ...

  8. trie树——【吴传之火烧连营】

    突然发现好像没有讲过一种叫做tire树的神奇东西. 问题描述: 题目描述 [题目背景] 蜀汉章武元年(221年),刘备为报吴夺荆州.关羽被杀之仇,率大军攻吴.吴将陆逊为避其锋,坚守不战,双方成对峙之势 ...

  9. nginx完美支持thinkphp3.2.2(需配置URL_MODE=>3 rewrite兼容模式)

    来源:http://www.thinkphp.cn/topic/26637.html 环境:nginx 1.6,thinkphp3.2.2 第一步,修改server块 server { listen ...

  10. 网站搭建-云服务器是什么-云服务器ECS是什么

    学习上瘾了,本博客关闭,后期再总结整理.