bzoj2973转移矩阵构造法!

/*
构造单位矩阵(转移矩阵)
给定n*m网格,每个格子独立按照长度不超过6的操作串循环操作
对应的操作有
    0-9:拿x个石头到这个格子
    nwse:把这个格子的石头推移到相邻格子
    d:清空该格石子
开始时网格是空的,问t秒后石头最多的格子里有多少个石子

t很大,并且每次操作后格子里的石头是线性变化的,所以用矩阵来加速递推 
将n*m网格表示成为(i-1)*m+j的一维数组,那么这个数组对应的转移矩阵大小应该是(nm)^2

然后由于每个格子的操作串最长只有6,并且1-6的最小公倍数是60,所以所有格子的操作60秒一个循环
那么求出每秒的转移矩阵,用Ai表示第i秒的转移矩阵,(1<=i<=60) 
另A=mul(Ai),t=p*60+r,那么结果状态就是A^p*mul(A1,Ar);

那么如何将操作对应到转移矩阵的构造上?
考虑转移矩阵的运行原理:可以将F'[j]=F[i]*A[i][j],即将原数组的F[i]*A[i][j]然后加到F'[j]上
    0-9:凭空把x个石头转移到第i个格子上,令F[0]=1,A[0][num(i,j)]=x,如此即可完成状态转移 
    nwse:距离n操作:把第i行的石头转移到第i-1行,即把num[i][j]的石头转移到num[i-1][j],那么令A[num[i][j]][num[i-1][j]]=1即可
    保证要使F[0]=1,那么将A[0][0]设置为1
    A的其它数值都为0
F数组就是[1,0,0,0,0,0,0,0,...0]
矩阵快速幂加速计算
*/ #include<bits/stdc++.h> using namespace std; #define P 65 #define ll long long int n,m,t,q,i,j,k,x,y,N; int id[P][P],a[P][P],l[P];// char b[P][P]; ll ans; struct mat{ ll a[P][P]; mat(){memset(a,0,sizeof a);} mat operator*(mat b){//重载矩阵乘法 mat c; for(int i=0;i<n;i++) for(int j=0;j<n;j++) for(int k=0;k<n;k++) c.a[i][j]+=a[i][k]*b.a[k][j]; return c; } }one,A[P],pre[P],B,C,D; int main(){ scanf("%d%d%d%d",&n,&m,&t,&q); char ch; for(int i=1;i<=n;i++)//输入每个格子对应的操作序号 for(int j=1;j<=m;j++) cin>>ch,a[i][j]=ch-'0',id[i][j]=++N; N++; for(int i=0;i<q;i++){//输入q行不同的操作序列 scanf("%s",b[i]+1),l[i]=strlen(b[i]+1); b[i][0]=b[i][l[i]];//循环点处理一下 } for(int i=0;i<n;i++)one.a[i][i]=1;//构造单位矩阵 pre[0]=one; char c; for(int i=1;i<=60;i++){//求出每个时间点对应的状态转移矩阵 A[i].a[0][0]=1;//对于第1个点 for(int j=1;j<=n;j++) for(int k=1;k<=m;k++){ //求出第[j,k]块的序号,就是a[j][k],在第i个时间点对应的操作c c=b[a[j][k]][i%l[a[j][k]]]; if(c>='0' && c<='9'){//数字 A[i].a[id[j][k]][0]=c-'0'; A[i].a[id[j][k]][id[j][k]]++; } if(c=='N'){//向各个方向转移 x=j-1,y=k; if(x>=1) A[i].a[id[x][y]][id[j][k]]++; } if(c=='S'){ x=j+1,y=k; if(x<=n) A[i].a[id[x][y]][id[j][k]]++; } if(c=='W'){ x=j,y=k-1; if(y>=1) A[i].a[id[x][y]][id[j][k]]++; } if(c=='E'){ x=j,y=k+1; if(y<=m) A[i].a[id[x][y]][id[j][k]]++; } } pre[i]=A[i]*pre[i-1];//把所有时间点的矩阵乘起来 } B=pre[60],C=one; for(k=t/60;k;k>>=1){//快速幂 if(k&1)C=C*B; B=B*B; } D.a[0][0]=1,D=pre[t%60]*C*D;//把余下时间的矩阵呈上去 for(int i=1;i<N;i++)//由于F数组只有第0项为1,那么直接求D[i][0]的最大值即可! if(ans<D.a[i][0]) ans=D.a[i][0]; printf("%lld",ans); }

 

转载于:https://www.cnblogs.com/zsben991126/p/10527213.html

BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值