题解 P3666 【[USACO17OPEN]COWBASIC 奶牛BASIC】
题解
初看这道题感觉十分难做,除了麻烦的语法分析,还需要优化循环。
官方题解
循环不嵌套
此时直接模拟即可,最多只有50个循环。
只有一个变量
当只有一个变量的时候,可以得到一个通项公式,但实际并不实用。具体可参考官方题解。
使用矩阵乘法
理论上,通过公式也可以做这道题,但是用矩阵乘法更加简洁。
构造转移矩阵
矩阵中包含各个变量的转移关系。对于矩阵A和B,先后执行等价于乘以A*B。而A循环n次则等价于乘以
对于nsq = ( nsq ) + ( ( n ) + ( ( n ) + ( 1 ) ) ),构造矩阵为
1 n nsq
1 1
n 1
nsq 1 2 1
注意转移没有被赋值的量。
另外,直接忽略表达式中的括号,因为加法没有优先级问题。
处理嵌套循环
维护一个栈,保存每层循环的结果和循环次数。
有新循环时,压入一个单位矩阵;循环退出时,弹出栈顶,执行快速幂,并与下一层相乘。
时间复杂度
矩阵大小不超过100x100,最多有50个循环,每个循环最多计算
代码
#include<bits/stdc++.h>
using namespace std;
const int N=105,MOD=1e9+7;
int n,cnt[N];
//cnt[]保存每层循环的次数
struct matrix
{
long long mat[N][N];
matrix()
{
memset(mat,0,sizeof(mat));
}
matrix operator*(const matrix& rhs)const
{
matrix ans;
for(int i=1;i<=n;i++)
for(int k=1;k<=n;k++)
for(int j=1;j<=n;j++)
{
ans.mat[i][j]=(ans.mat[i][j]+mat[i][k]*rhs.mat[k][j])%MOD;
assert(ans.mat[i][j]>=0);
}
return ans;
}
matrix operator*=(const matrix& rhs)
{
return *this=*this*rhs;
}
}S[N];
//矩阵栈
matrix I()
{
matrix ans;
for(int i=1;i<=n;i++)
ans.mat[i][i]=1;
return ans;
}
//单位矩阵
matrix qpow(matrix a,int b)
{
matrix ans=I();
do
{
if(b&1)
ans*=a;
a*=a;
}
while(b/=2);
return ans;
}
//矩阵快速幂
string code[N];
template<typename T>
inline T get_token(const string& s)
{
stringstream ss(s);
T ret;
ss>>ret;
return ret;
}
//将字符串s中的下一个内容转换为T
int main()
{
map<string,int> var;
//保存变量名对应的编号
var["1"]=1;
//没什么用
int lines=0;
n=1;
while(getline(cin,code[++lines]))
if(code[lines].find('=')!=string::npos)
{
string name=get_token<string>(code[lines]);
//题目保证所有变量都会为左值
if(var.find(name)==var.end())
var[name]=++n;
}
lines--;
int sp=1;
S[sp]=I();
for(int i=1;i<=lines;i++)
if(code[i].substr(0,6)=="RETURN")
cout<<S[sp].mat[var[get_token<string>(code[i].substr(6))]][1]<<endl;
//运算结果保存在矩阵第一列中
else
if(code[i].find("MOO")!=string::npos)
//新循环
{
S[++sp]=I();
cnt[sp]=get_token<int>(code[i]);
}
else
if(code[i].find('}')!=string::npos)
//循环结束
{
S[sp-1]=qpow(S[sp],cnt[sp])*S[sp-1];
sp--;
}
else
{
matrix now;
int row=var[get_token<string>(code[i])],p=code[i].find('=')+1;
stringstream ss(code[i].substr(p));
string token;
while(ss>>token)
{
if(isdigit(token[0]))
now.mat[row][1]+=get_token<int>(token);
//累加常数
else
if(isalpha(token[0]))
now.mat[row][var[token]]++;
//累加变量
}
for(int i=1;i<=n;i++)
if(i!=row)
now.mat[i][i]=1;
//转移未赋值的量
S[sp]=now*S[sp];
}
return 0;
}
语法分析时应该充分利用空格,同时也要防止多余的空格。