ACM大一寒假集训1.3
【模板】后缀排序
看这篇学的,觉得讲的挺好后缀数组算法总结
char s[maxn];
int sa[maxn],x[maxn],y[maxn],c[maxn],n;
void build(int m)
{
int i;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=s[i]]++;
for(i=1;i<m;i++) c[i] += c[i-1];
for(i=n-1;i>=0;i--)
sa[--c[x[i]]]=i;
// for (int i=0;i<n;i++) cout<<sa[i]<<endl;
for(int k=1;k<n;k<<=1)
{ //倍增,将k凑成k*2长度,k>=n就没必要了
int p=0;
for(i=n-1;i>=n-k;i--) y[p++]=i;
for(i=0;i<n;i++)
if(sa[i]>=k) y[p++]=sa[i]-k;//sa[i]>=k的位置才能做第二关键字
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--)
sa[--c[x[y[i]]]]=y[i];//两次排序后的数组
//下标是排名,值为首字母位置:sa,y
//下标是位置,值是排名:x
swap(x,y);//y:第一关键字排序 x:上一轮的rank
p=1;x[sa[0]]=0;
for(i=1;i<n;i++)
if(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])
x[sa[i]]=p-1;
else x[sa[i]]=p++;
if(p>=n) break;
m=p;
}
return ;
}
int main()
{
scanf("%s",s);
n=strlen(s);
build(10000);
for (int i=0;i<n;i++)
cout<<sa[i]+1<<" ";
return 0;
}
难度:3
T1洛谷P2870 [USACO07DEC]Best Cow Line G
明显的贪心是:比较首字符和尾字符的大小,取小的。
但对于大小相同的就不好判断了,我们用f[i]表示从ii开始的后缀,用g[i]表示从ii开始的前缀。那么遇到str[L]=str[R]时,比较f[L]和g[R]即可,这个可以用后缀数组排序实现。
难度:3
T2P4051 [JSOI2007]字符加密
把字符串首尾相接,扩展为原来的两倍,就能按后缀数组的方法处理
难度:3
矩阵快速幂模板
#include<bits/stdc++.h>
using namespace std;
long long mod=1000000007,n,k;
struct node
{
long long m[200][200];
}a;
node x(node a,node b)
{
node t;
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
{
t.m[i][j]=0;
for (int k=0;k<n;k++)
t.m[i][j]=(t.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
}
return t;
}
node ksm(node a,long long k)
{
node ret=a,b=a;k--;
while (k>0)
{
if (k%2==1)
ret=x(b,ret);
b=x(b,b);
k=k/2;
}
return ret;
}
int main()
{
scanf("%lld%lld",&n,&k);
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
scanf("%lld",&a.m[i][j]);
a=ksm(a,k);
for (int i=0;i<n;i++)
{
for (int j=0;j<n;j++)
printf("%lld ",a.m[i][j]);
printf("\n");
}
return 0;
}
T3P1962 斐波那契数列
构造矩阵base=
初始矩阵ans=
Fn为ans*base^(n-2),用矩阵快速幂即可
难度:2
(广义斐波那契数列是更一般的形式)
T4矩阵加速(数列)
和上一题差不多,构造个矩阵就行了
难度:2
矩阵经典问题有向图的可达矩阵
例题:HDU2167
难度:3