给个洛谷网址吧qwq传送门
这题先说80分做法吧(洛谷上70分)
显然我们可以对于每一个时间构造一个矩阵表示变化情况,然后我们注意到,因为
l
i
,
j
=
{
3
,
7
,
9
}
l_{i,j}=\{3,7,9\}
li,j={3,7,9},所以每63个时间是一个循环,那么我们构造63个转移矩阵,然后连乘起来,再快速幂优化,有点类似于沼泽鳄鱼那道题
简单放一下70分代码
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=70;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m,x,p,t;
int to[N][N],tot;
string l[N][N],s;
int sz;
struct matrix{
int a[N][N];
matrix(){memset(a,0,sizeof(a));}
void build(){
Rep(i,1,sz)a[i][i]=1;
}
void print(){
Rep(i,1,sz)
Rep(j,1,sz)printf("%d%c",a[i][j],j==sz?'\n':' ');
}
matrix operator * (const matrix &x)const {
matrix res;
Rep(i,1,sz)
Rep(j,1,sz)
Rep(k,1,sz)
res.a[i][j]=1ll*(res.a[i][j]+1ll*a[i][k]*x.a[k][j]%p)%p;
return res;
}
}A[N],B,ans;
matrix Qpow(matrix base,int ind){
matrix res;
res.build();
while(ind){
if(ind&1)res=res*base;
base=base*base;
ind>>=1;
}
return res;
}
void output(matrix A){
Rep(i,1,n){
int last=-1e9,tim=0;
Rep(j,1,m){
int pos=to[i][j];
if(A.a[1][pos]==last)tim++;
else{
if(tim)printf("%d %d ",tim,last);
last=A.a[1][pos];
tim=1;
}
}
if(tim)printf("%d %d\n",tim,last);
}
}
int main()
{
read(n),read(m),read(x),read(p),read(t);
Rep(i,1,n)
Rep(j,1,m){
int len;
read(len);
cin>>s;
Rep(k,1,63/len)l[i][j]+=s;
}
Rep(i,1,n)
Rep(j,1,m)
to[i][j]=++tot;
sz=n*m+1;
Rep(k,0,62){
Rep(i,1,n)
Rep(j,1,m)
switch(l[i][j][k]){
case 'C':
A[k+1].a[to[i][j]][to[i][j]]=1;
A[k+1].a[sz][to[i][j]]=x;
break;
case 'U':
if(i>1)A[k+1].a[to[i][j]][to[i-1][j]]++;
break;
case 'D':
if(i<n)A[k+1].a[to[i][j]][to[i+1][j]]++;
break;
case 'L':
if(j>1)A[k+1].a[to[i][j]][to[i][j-1]]++;
break;
case 'R':
if(j<m)A[k+1].a[to[i][j]][to[i][j+1]]++;
break;
}
A[k+1].a[sz][sz]=1;
}
ans.a[1][sz]=1;
if(t<=63){
Rep(i,1,t)ans=ans*A[i];
output(ans);
return 0;
}
B.build();
Rep(i,1,63)B=B*A[i];
ans=ans*Qpow(B,t/63);
Rep(i,1,t%63)ans=ans*A[i];
output(ans);
return 0;
}
建议先写出70分做法,这样对于思考满分做法有很大帮助,而且满分代码只需要在70分代码上简单改动就可以了
接下来我们考虑满分做法,我们考虑我们最开始设计的那些转移矩阵,其实非常稀疏,除了1那一行以外每一行就一个数,那么相应的引申出两种解决方案,一个是用链表来存矩阵,好像大家都是这么做的,但是我tcl,只能写更弱的方法
我们发现,一些水珠经过一段时间去了哪里我们是确定的,所以我们把原来的矩阵转化成一个线性表,
A
.
l
i
n
k
[
i
]
A.link[i]
A.link[i]表示重新标号之后编号为
i
i
i的点最后去了哪里,那么显然在两个"矩阵"
A
A
A和
B
B
B合并成一个新的"矩阵"的时候,
C
.
l
i
n
k
[
i
]
=
B
.
l
i
n
k
[
A
.
l
i
n
k
[
i
]
]
C.link[i]=B.link[A.link[i]]
C.link[i]=B.link[A.link[i]],这个还是挺好理解的吧qwq,那么接下来考虑产生水的情况,我们用
A
.
c
r
e
a
t
e
[
i
]
A.create[i]
A.create[i]表示"矩阵"
A
A
A在这个时间内点
i
i
i处新产生了多少水,那么我们考虑"矩阵"
A
A
A,
B
B
B合并成
C
C
C之后,
i
i
i点所含的水珠格式
C
.
v
a
l
[
i
]
C.val[i]
C.val[i],首先应该包含
B
.
c
r
e
a
t
e
[
i
]
B.create[i]
B.create[i],同时,对于所有
B
.
l
i
n
k
[
j
]
=
i
B.link[j]=i
B.link[j]=i的
j
j
j,
C
.
v
a
l
[
i
]
C.val[i]
C.val[i]应该还要包含
A
.
v
a
l
[
j
]
A.val[j]
A.val[j],因为他从
j
j
j经过
l
i
n
k
link
link的作用,到达了
i
i
i,那么我们最后发现这个
c
r
e
a
t
e
create
create数组可以融入到
v
a
l
val
val中,也必须这样,因为当我们后面做快速幂的时候,是一下乘上表示一段的"矩阵",但是
c
r
e
a
t
e
create
create只能表示一个时间的情况。
所以如果你刚才写的是重载运算符的写法,你只需要把重载乘号的地方改一下,建立"矩阵"的部分改一下,还有建立单位矩阵的方式改一下就好了
对于新的"矩阵"的"单位矩阵 I I I",我们让 I . v a l [ i ] = 0 , I . l i n k [ i ] = i I.val[i]=0,I.link[i]=i I.val[i]=0,I.link[i]=i就可以了
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=105;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m,x,p,t;
int to[N][N],tot;
string l[N][N],s;
int sz;
struct matrix{
int link[N*N],val[N*N];
matrix(){memset(link,0,sizeof(link)),memset(val,0,sizeof(val));}
void build(){
Rep(i,1,sz)link[i]=i;
}
void print(){
Rep(i,1,n)
Rep(j,1,m)
printf("%d%c",val[to[i][j]],j==m?'\n':' ');
}
matrix operator * (const matrix &x)const {
matrix res;
Rep(i,1,sz)
res.link[i]=x.link[link[i]];
Rep(i,1,sz)res.val[x.link[i]]+=val[i],res.val[x.link[i]]%=p;
Rep(i,1,sz)res.val[i]+=x.val[i],res.val[i]%=p;
return res;
}
}A[N],B,ans;
matrix Qpow(matrix base,int ind){
matrix res;
res.build();
while(ind){
if(ind&1)res=res*base;
base=base*base;
ind>>=1;
}
return res;
}
void output(matrix A){
Rep(i,1,n){
int last=-1e9,tim=0;
Rep(j,1,m){
int pos=to[i][j];
if(A.val[pos]==last)tim++;
else{
if(tim)printf("%d %d ",tim,last);
last=A.val[pos];
tim=1;
}
}
if(tim)printf("%d %d\n",tim,last);
}
}
int main()
{
read(n),read(m),read(x),read(p),read(t);
Rep(i,1,n)
Rep(j,1,m){
int len;
read(len);
cin>>s;
Rep(k,1,63/len)l[i][j]+=s;
}
Rep(i,1,n)
Rep(j,1,m)
to[i][j]=++tot;
sz=n*m;
Rep(k,0,62){
Rep(i,1,n)
Rep(j,1,m)
switch(l[i][j][k]){
case 'C':
A[k+1].link[to[i][j]]=to[i][j];
A[k+1].val[to[i][j]]+=x;
break;
case 'U':
if(i>1)A[k+1].link[to[i][j]]=to[i-1][j];
break;
case 'D':
if(i<n)A[k+1].link[to[i][j]]=to[i+1][j];
break;
case 'L':
if(j>1)A[k+1].link[to[i][j]]=to[i][j-1];
break;
case 'R':
if(j<m)A[k+1].link[to[i][j]]=to[i][j+1];
break;
}
}
if(t<=63){
Rep(i,1,t)ans=ans*A[i];
output(ans);
return 0;
}
B.build();
Rep(i,1,63)B=B*A[i];
ans=Qpow(B,t/63);
Rep(i,1,t%63)ans=ans*A[i];
output(ans);
return 0;
}
本文详细解析了一道洛谷竞赛题的80分和满分做法,通过构建转移矩阵和优化算法,实现对复杂状态的高效求解。文章分享了代码实现细节,包括使用矩阵运算和快速幂优化,以及如何将问题转化为线性表形式,简化计算过程。
1078

被折叠的 条评论
为什么被折叠?



