倍增&矩阵乘法 专题复习

PreWords

这两个基础算法我就不多说啦,但是还是要介绍一下" 广义矩阵 "乘法

其实就是把矩阵换成取\(max\),然后都一样。。。

据神仙LBC说:这不显然是对的吗!

\[\
\]

\[\
\]

[usaco2007 Nov] relays 奶牛接力跑

离散一下,然后套矩阵乘法\(a[i][j]\)记录从\(i\)出发到\(j\)的最小答案,快速幂即可

const int N=410,P=1e4+7;

int n,m,s,t;
int a[N],b[N],c[N];
int h[N],cnt; inline void chk(ll &a,ll b) { ((a>b)&&(a=b)); } struct Mat{
ll a[N][N];
void init(){ memset(a,63,sizeof a); }
Mat operator * (const Mat x) const {
Mat res; res.init();
rep(i,1,cnt) rep(j,1,cnt) rep(k,1,cnt) chk(res.a[i][k],a[i][j]+x.a[j][k]);
return res;
}
} res,x; int main() {
n=rd(),m=rd(),s=rd(),t=rd();
rep(i,1,m) {
c[i]=rd(),a[i]=rd(),b[i]=rd();
h[++cnt]=a[i];
h[++cnt]=b[i];
}
sort(h+1,h+cnt+1);
cnt=unique(h+1,h+cnt+1)-h-1;
x.init();
rep(i,1,m) {
a[i]=lower_bound(h+1,h+cnt+1,a[i])-h;
b[i]=lower_bound(h+1,h+cnt+1,b[i])-h;
chk(x.a[a[i]][b[i]],c[i]);
chk(x.a[b[i]][a[i]],c[i]);
}
s=lower_bound(h+1,h+cnt+1,s)-h;
t=lower_bound(h+1,h+cnt+1,t)-h;
n--;
res=x;
while(n) {
if(n&1) res=res*x;
x=x*x;
n>>=1;
}
printf("%lld\n",res.a[s][t]);
}

\[\
\]

\[\
\]

[BZOJ4773] 负环

基本类似,但是由于这题要二分答案,直接套是\(log^2\),用倍增替换二分即可

int n,m;

inline void chk(int &a,int b) { ((a>b)&&(a=b)); }

struct Mat{
int a[N][N];
void init(){
memset(a,63,sizeof a);
}
Mat operator * (const Mat x) const {
Mat res; res.init();
rep(i,1,n) rep(j,1,n) rep(k,1,n) chk(res.a[i][k],a[i][j]+x.a[j][k]);
return res;
}
}st,Pow[10]; int main() {
n=rd(),m=rd();
st.init();
rep(i,1,n) st.a[i][i]=0;
rep(i,1,m) {
int a=rd(),b=rd(),c=rd();
chk(st.a[a][b],c);
}
Pow[0]=st;
rep(i,1,9) Pow[i]=Pow[i-1]*Pow[i-1];
int fl=0;
rep(i,1,n) if(Pow[9].a[i][i]<0) fl=1;
if(!fl) return puts("0"),0;
Mat now=st;
int res=2;
drep(i,9,0) {
Mat t=now*Pow[i];
fl=0;
rep(j,1,n) if(t.a[j][j]<0) fl=1;
if(!fl) now=t,res+=1<<i;
}
printf("%d\n",res);
}

\[\
\]

\[\
\]

[BZOJ4417] [Shoi2013]超级跳马

有奇偶性问题?开两倍的数组记录当前这一列是奇数还是偶数即可

最后注意由于要保证跳了\(m\)格,还要减去跳了\(m-1\)格以内的答案

const int N=110,P=30011;

int n,m;

struct Mat {
int a[N][N];
void init(){ memset(a,0,sizeof a); }
Mat operator * (const Mat x) const {
Mat res; res.init();
rep(i,1,n*2) rep(j,1,n*2) rep(k,1,n*2) res.a[i][k]=(res.a[i][k]+a[i][j]*x.a[j][k])%P;
return res;
}
}res,res2,x,st; int main() {
n=rd(),m=rd();
rep(i,1,n) {
if(i>1) x.a[i][i-1]++;
if(i<n) x.a[i][i+1]++;
x.a[i][i]++;
x.a[i][i+n]++;
x.a[i+n][i]++;
}
m--;
st=x;
res=x;m--;
int t=m;
for(;m;m>>=1,x=x*x) if(m&1) res=res*x;
m=t;
if(m>0) {
x=st;
res2=x;m--;
for(;m;m>>=1,x=x*x) if(m&1) res2=res2*x;
}
int ans=(res.a[1][n]+res.a[1][n*2]-res2.a[1][n]-res2.a[1][n*2])%P;
ans=(ans%P+P)%P;
printf("%d\n",ans);
}

\[\
\]

\[\
\]

[BZOJ2093] [Poi2010]Frog

这个题是一个标准的倍增吧。。。

关于预处理第k远

尺取两边 \(i\) 第一个 \(j\) 满足 距离在 \(abs(a[j]-a[i])\) 以内的点个数 $ \ge k$,当然也可以二分

然后比较两个谁近

尺取过程可以参考代码

以下是二分

 rep(i,1,n) {
reg int l=max(1,i-k),r=min((int)i,n-k),res1=-1;
while(l<=r) {
int mid=(l+r)>>1;
if(a[mid+k]<=a[i]+(a[i]-a[mid])) l=mid+1,res1=mid;
else r=mid-1;
}
l=max(k+1,(int)i),r=min(i+k,n);
reg int res2=-1;
while(l<=r) {
int mid=(l+r)>>1;
if(a[mid-k]>=a[i]-(a[mid]-a[i])) r=mid-1,res2=mid;
else l=mid+1;
}
if(res1==-1 || (res2!=-1 && a[res2]-a[i]<a[i]-a[res1])) Nxt[i][0]=res2;
else Nxt[i][0]=res1;
}
}

以下是尺取

const int N=1e6+10;

int n,k;
ll m;
ll a[N];
int Nxt[2][N];
int ans[N];
int res1[N],res2[N]; int main() {
n=rd(),k=rd(); m=rd();
rep(i,1,n) a[i]=rd();
int p=1;
rep(i,1,n) {
if(a[k+1]>a[i]+a[i]-a[1]) res1[i]=-1;
else {
while(p<n-k && p<i-1 && a[p+k+1]<=a[i]+a[i]-a[p+1]) p++;
res1[i]=p;
}
}
p=n;
drep(i,n,1) {
if(a[n-k]<a[i]+a[i]-a[n]) res2[i]=-1;
else {
while(p>k+1 && p>i+1 && a[p-k-1]>=a[i]+a[i]-a[p-1]) p--;
res2[i]=p;
}
}
rep(i,1,n) {
if(res1[i]==-1 || (res2[i]!=-1 && a[res2[i]]-a[i]<a[i]-a[res1[i]])) Nxt[0][i]=res2[i];
else Nxt[0][i]=res1[i];
}
rep(i,1,n) ans[i]=i;
int cur=1;
for(reg int i=0;(1ll<<i)<=m;++i) {
cur^=1;
if(i) for(reg int j=1;j<=n;++j) Nxt[cur][j]=Nxt[!cur][Nxt[!cur][j]];
if(m&(1ll<<i)) for(reg int j=1;j<=n;++j) ans[j]=Nxt[cur][ans[j]];
}
rep(i,1,n) printf("%d ",ans[i]);
}

\[\
\]

\[\
\]

[BZOJ4082] [Wf2014]Surveillance

仿佛是一个比较经典的问题?

断环成链

\(nxt[i][j]\)记录当前从\(i\)开始,覆盖\(2^j\)个区间的能覆盖的最远位置

每一个点开始倍增到第一个能跨越长度\(n\)的位置,复杂度\(O(n \ log \ n)\)

但事实上可以带权并查集维护,\(O(n \ \alpha(n) )\)

倍增

const int N=2e6+10;

int n,m;
struct Node {
int l,r;
bool operator < (const Node __) const {
return l<__.l;
}
} Seg[N];
int fa[21][N];
int cnt; int main() {
n=rd(),m=rd();
rep(i,1,m) {
int l=rd(),r=rd();
if(l<=r) {
Seg[++cnt]=(Node){l,r};
Seg[++cnt]=(Node){l+n,r+n};
} else Seg[++cnt]=(Node){l,r+n};
}
sort(Seg+1,Seg+cnt+1);
int p=1,ma=0;
rep(i,1,n*2+1) {
while(p<=cnt && Seg[p].l<=i) {
ma=max(ma,Seg[p++].r);
}
fa[0][i]=max(ma+1,(int)i);
}
rep(i,1,20) rep(j,1,n*2+1) fa[i][j]=fa[i-1][fa[i-1][j]];
int ans=1e9;
rep(i,1,n) {
int p=i,res=0;
drep(j,20,0) if(fa[j][p]<i+n) p=fa[j][p],res+=1<<j;
if(fa[0][p]>=i+n) ans=min(ans,res+1);
}
if(ans<1e8) printf("%d\n",ans);
else puts("impossible");
}

并查集

const int N=2e6+10;

int n,m;
int ma[N],fa[N],dis[N];
int Find(int x) {
if(x==fa[x]) return x;
int f=fa[x];
fa[x]=Find(fa[x]),dis[x]+=dis[f];
return fa[x];
} int main() {
n=rd(),m=rd();
rep(i,1,m) {
int l=rd(),r=rd();
chk(ma[l],((l<=r)?r:r+n));
}
reg int ans=1e9,ma=0;
for(reg int i=1;i<=n*2+1;++i) fa[i]=i;
for(reg int i=1;i<=n*2;++i) {
chk(ma,::ma[i]);
if(i>n) {
int t=i-n;
if(Find(t)>=i) chkmin(ans,dis[t]);
}
if(ma>=i) fa[i]=ma+1,dis[i]=1; //并查集维护最远覆盖
}
if(ans<=n) printf("%d\n",ans);
else puts("impossible");
}

\[\
\]

\[\
\]

[BZOJ2085] [Poi2010]Hamsters

我并不会写,只能提供一组Hack数据

Input:

3 4

ab

cd

abcde

Output:

7

(abcdeab)

希望我没有读错题

\[\
\]

\[\
\]

[BZOJ4569][Scoi2016]萌萌哒

好题!

倍增维护并查集合并

一个倍增数组\(fa[i][j]\)维护从\(i\)开始长度为\(2^j\)的这一段与那一段长度相同的并在一起

将两端区间\(l1,r2,l2,r2\)用倍增剖开,在那一层的倍增数组上用并查集合并

最后每次将\(fa[i][j]\)向\(fa[i][j-1],fa[i+(1<<(j-1))][j-1]\)递推即可

int n,m;
struct UFS{
int fa[N];
int Find(int x) { return fa[x]==x?x:fa[x]=Find(fa[x]); }
void init(){ rep(i,1,n) fa[i]=i; }
void merge(int x,int y) {
fa[Find(x)]=Find(y);
}
} B[18];
int LOG;
int main(){
n=rd(),m=rd();
for(LOG=0;(1<<LOG)<=n;LOG++) B[LOG].init();
LOG--;
rep(i,1,m) {
int l1=rd(),r1=rd();
int l2=rd();rd();
if(l1==l2) continue;
int len=r1-l1+1;
drep(j,LOG,0) {
if(len>=(1<<j)) {
B[j].merge(l1,l2);
l1+=1<<j,l2+=1<<j;
len-=1<<j;
}
}
}
drep(i,LOG,1) {
int len=(1<<(i-1));
rep(j,1,n) {
int f=B[i].Find(j);
B[i-1].merge(j,f);
B[i-1].merge(j+len,f+len);
}
}
int cnt=0;
rep(i,1,n) if(B[0].Find(i)==i) cnt++;
ll ans=1;
rep(i,1,cnt-1) ans=ans*10%P;
ans=ans*9%P;
printf("%lld\n",ans);
}

倍增&矩阵乘法 专题复习的更多相关文章

  1. 【loj2325】「清华集训 2017」小Y和恐怖的奴隶主 概率dp+倍增+矩阵乘法

    题目描述 你有一个m点生命值的奴隶主,奴隶主受伤未死且当前随从数目不超过k则再召唤一个m点生命值的奴隶主. T次询问,每次询问如果如果对面下出一个n点攻击力的克苏恩,你的英雄期望会受到到多少伤害. 输 ...

  2. CF781D Axel and Marston in Bitland [倍增 矩阵乘法 bitset]

    Axel and Marston in Bitland 好开心第一次补$F$题虽然是$Div.2$ 题意: 一个有向图,每条边是$0$或$1$,要求按如下规则构造一个序列然后走: 第一个是$0$,每次 ...

  3. 4.28 省选模拟赛 负环 倍增 矩阵乘法 dp

    容易想到 这个环一定是简单环. 考虑如果是复杂环 那么显然对于其中的第一个简单环来说 要么其权值为负 如果为正没必要走一圈 走一部分即可. 对于前者 显然可以找到更小的 对于第二部分是递归定义的. 综 ...

  4. 【POJ3613】Cow Relays 离散化+倍增+矩阵乘法

    题目大意:给定一个 N 个顶点,M 条边的无向图,求从起点到终点恰好经过 K 个点的最短路. 题解:设 \(d[1][i][j]\) 表示恰好经过一条边 i,j 两点的最短路,那么有 \(d[r+m] ...

  5. [CF1067D]Computer Game[凸包/斜率优化+倍增+矩阵乘法]

    题意 你有 \(n\) 个任务,初始收益为 \(a\) ,共 \(t\) 轮游戏,每轮可以选择完成一个任务(可以做多次),完成之后可以给任意任务升级,升级之后的任务收益为 \(b\) ,每个任务还有完 ...

  6. 矩阵乘法优化DP复习

    前言 最近做毒瘤做多了--联赛难度的东西也该复习复习了. Warning:本文较长,难度分界线在"中场休息"部分,如果只想看普及难度的可以从第五部分直接到注意事项qwq 文中用(比 ...

  7. BZOJ4386[POI2015]Wycieczki——矩阵乘法+倍增

    题目描述 给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种.将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点. 输入 第 ...

  8. CF1067D. Computer Game(斜率优化+倍增+矩阵乘法)

    题目链接 https://codeforces.com/contest/1067/problem/D 题解 首先,如果我们获得了一次升级机会,我们一定希望升级 \(b_i \times p_i\) 最 ...

  9. poj3613:Cow Relays(倍增优化+矩阵乘法floyd+快速幂)

    Cow Relays Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7825   Accepted: 3068 Descri ...

随机推荐

  1. Python面向对象封装案例

    01. 封装 封装 是面向对象编程的一大特点 面向对象编程的 第一步 —— 将 属性 和 方法 封装 到一个抽象的 类 中 外界 使用 类 创建 对象,然后 让对象调用方法 对象方法的细节 都被 封装 ...

  2. datax分析与思考(一)

    Datax 总体流程图 先看执行的第一个步骤: 在最上层抽象类,这个里面相当于获取全局公共信息,java入口部分就是这个Engine的main方法直接启动 Engine 启动 com.alibaba. ...

  3. v2 配置

    记录一下怕自己忘记了 v2配置 v2加速 cdn加速 hostwind 服务器 namesilo 域名网站

  4. 查看mysql字符集、修改数据库、数据表、字段字符集

    查看所有表的字符集 SELECT table_name, table_type, engine, version, table_collation FROM information_schema.ta ...

  5. 【转载】Windows系统电脑如何更换盘符号

    在笔记本电脑或者办公电脑的使用过程中,有时候需要更换盘符号,例如在重装系统后,硬盘相应的分区盘符号可能会发生错乱变化,此时如果想更换回重装系统之前的盘符号,可以通过计算机管理里面的磁盘管理来实现更换盘 ...

  6. HTML5实现无刷新修改URL

    前言 今天在做一个vue的搜索功能,需要从搜索结果页面跳转到细节页面,然后点击返回还能返回到刚刚的结果页面,如果只用window.history.go(-1)当然会重新刷新搜索页面,当然是不行的. 我 ...

  7. Web前端2019面试总结2

    1.js继承: 想要继承,就必须要提供个父类(继承谁,提供继承的属性) 组合继承(组合原型链继承和借用构造函数继承)(常用) 重点:结合了两种模式的优点,传参和复用 特点:1.可以继承父类原型上的属性 ...

  8. Elasticsearch使用DateHistogram聚合

    date_histogram是按照时间来构建集合(桶)Buckts的,当我们需要按照时间进行做一些数据统计的时候,就可以使用它来进行时间维度上构建指标分析.     在前面几篇中我们用到的hitogr ...

  9. 数据结构与算法—Trie树

    Trie,又经常叫前缀树,字典树等等.它有很多变种,如后缀树,Radix Tree/Trie,PATRICIA tree,以及bitwise版本的crit-bit tree.当然很多名字的意义其实有交 ...

  10. ApplicationContext的名称解释

    如果说BeanFactory是Spring的心脏,那么Application就是完整的身躯.ApplicationContext就是由BeanFactory派生出来的. 1.ApplicationCo ...