T1
[BZOJ2462][BeiJing2011]Matrix
题解:
今天对于这道题有一个很清晰的思路,想着把一维hash写对,可是一维一拍一个错?!后来把一维的交上然后A了,再拍也是对的,这都什么啊这都是。
其实很简单啦,就是把每一行建立一个hash,然后暴力寻找就行啦
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ull unsigned long long
using namespace std;
const int base=23,mod=998244353;
int n,m,a,b;
ull hash[1005][1005],mi[1005],ask[1005][1005];
char s[1005];
int main()
{
scanf("%d%d%d%d",&m,&n,&a,&b);
mi[0]=1;for (int i=1;i<=n;i++) mi[i]=mi[i-1]*base%mod;
for (int i=1;i<=m;i++)
{
scanf("%s",s+1);
ull ans=0;
for (int j=1;j<=n;j++)
{
ans=(ans*base+s[j])%mod;
hash[i][j]=ans;
}
}
int q;scanf("%d",&q);
while (q--)
{
for (int i=1;i<=a;i++)
{
scanf("%s",s+1);
ull ans=0;
for (int j=1;j<=b;j++)
{
ans=(ans*base+s[j])%mod;
ask[i][j]=ans;
}
}
bool fff=0;
for (int i=1;i<=m-a+1 && !fff;i++)
for (int j=1;j<=n-b+1 && !fff;j++)
if ((hash[i][j+b-1]-(hash[i][j-1]*mi[b]%mod)+mod)%mod==ask[1][b])
{
fff=1;
for (int z=1;z<a;z++)
if ((hash[i+z][j+b-1]-(hash[i+z][j-1]*mi[b]%mod)+mod)%mod!=ask[z+1][b])
{fff=0;break;}
}
if (fff) printf("1\n");else printf("0\n");
}
}
T2
[BZOJ2461][BeiJing2011]符环
题解:
loli带我们去北京?!
20pts的全’S’不难发现就是Catalan数列
30pts的爆搜就好,但我竟然写挂了
统计方案数当然要考虑DP。因为填好了前n个字符以后,根据它给定的信息,后n个就被唯一确定了,所以只要DP前n个即可。
然而还要求整个串是一个完整的括号匹配,所以左右括号的匹配情况也会影响状态。
于是用f[i][x][y][z]表示当前填写到了第i个字符,[1..i]这段区间里有x个未匹配的左括号,[n+1..n+i]这段区间里有y个未匹配的右括号和z个未匹配的左括号。
求的时候分情况讨论运用记忆化搜索就行啦
注意一在右边填上’)’,就要和右边[n+1..n+i]这段区间内的未匹配的左括号匹配,不要留着不匹配,因为早晚这些多余的左括号都要和右括号匹配的
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
char s[55];LL f[55][55][55][55];int n;
LL dfs(int t,int x,int y,int z)//x ( y ( z )
{
if (t>n) return y==0&&x==z;
if (f[t][x][y][z]!=-1) return f[t][x][y][z];
f[t][x][y][z]=0;
if (s[t]=='S')
{
if (x)
{
if (y) f[t][x][y][z]+=dfs(t+1,x-1,y-1,z);
else f[t][x][y][z]+=dfs(t+1,x-1,y,z+1);
}
f[t][x][y][z]+=dfs(t+1,x+1,y+1,z);
}
else
{
if (x) f[t][x][y][z]+=dfs(t+1,x-1,y+1,z);
if (y) f[t][x][y][z]+=dfs(t+1,x+1,y-1,z);
else f[t][x][y][z]+=dfs(t+1,x+1,y,z+1);
}
return f[t][x][y][z];
}
int main()
{
freopen("mobius.in","r",stdin);
freopen("mobius.out","w",stdout);
int T;
scanf("%d",&T);
while (T--)
{
scanf("%s",s+1);n=strlen(s+1);
memset(f,-1,sizeof(f));
printf("%lld\n",dfs(1,0,0,0));
}
}
T3
[BZOJ2553][BeiJing2011]禁忌
题解:
这是一道经典的AC自动机+矩阵优化dp的题目
首先我们提取题目中的关键字,可以转化成构造长为len的字符串,让ta在AC自动机上匹配到最大的禁忌串个数
首先把模式串建成AC自动机(或trie图),然后考虑在AC自动机上的转移。
对于每一步转移,都有两种情况:
1、子结点没被标记(即不是模式串的结尾单词),有1/alphabet的期望转移到这个子结点。
2、子结点被标记,有1/alphabet的期望值转移到根,并且用一个新结点记录答案。
这样我们可以考虑构造一个矩阵a[i][j],记录下来结点之间的转移关系。
然后矩阵自乘m次得到的矩阵ans,ans[0][cnt+1]就是答案。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#define LD long double
using namespace std;
int top=0,ch[100][30],n,L,A,fail[100];
char s[12][20];
bool ed[100];
struct node{
LD H[100][100];
node operator *(const node &a)const
{
node ans;
for (int i=0;i<=top;i++)
for (int j=0;j<=top;j++)
{
ans.H[i][j]=0.0;
for (int k=0;k<=top;k++)
ans.H[i][j]=ans.H[i][j]+H[i][k]*a.H[k][j];
}
return ans;
}
LD KSM(int p)
{
node ans=(*this);
node a=(*this);
p--;
while (p)
{
if (p&1) ans=ans*a;
a=a*a;
p>>=1;
}
return ans.H[0][top];
}
};
node H;
void build(int bh)
{
int now=0;
for (int i=0;i<strlen(s[bh]);i++)
{
int x=s[bh][i]-'a';
if (!ch[now][x]) ch[now][x]=++top;
now=ch[now][x];
}
ed[now]=1;
}
void make_fail()
{
queue<int> Q;
for (int i=0;i<A;i++) if (ch[0][i])
Q.push(ch[0][i]);
while (!Q.empty())
{
int now=Q.front(); Q.pop();
for (int i=0;i<A;i++)
{
if (!ch[now][i])
{
ch[now][i]=ch[fail[now]][i];
continue;
}
fail[ch[now][i]]=ch[fail[now]][i];
ed[ch[now][i]]|=ed[fail[ch[now][i]]]; //结尾结点的失配也是不能到达的
Q.push(ch[now][i]);
}
}
}
void make_H()
{
LD e=(double)1/A; //概率
for (int i=0;i<top;i++)
{
for (int j=0;j<A;j++)
if (ed[ch[i][j]]) //下一步转移到单词结尾
{
H.H[i][0]+=e; //不能继续转移,所以只能走到0结点
H.H[i][top]+=e; //增加伤害期望
}
else H.H[i][ch[i][j]]+=e; //转移概率
}
H.H[top][top]=1;
}
int main()
{
scanf("%d%d%d",&n,&L,&A);
for (int i=1;i<=n;i++)
{
scanf("%s",s[i]);
build(i);
}
make_fail();
make_H();
printf("%.10lf\n",(double)H.KSM(L));
return 0;
}
小结
今天的胡策是中游水平,怎么说呢,暴力写挂了,T1的模数选错了(这人品也是没谁了),GG