在很久很久很久以后,我终于又回到了coder的舞台。
那么我们来水一发dp吧!
好懒啊不想动
【原题】BZOJ 2958
题目描述
给出一个长度为N由B、W、X三种字符组成的字符串S,你需要把每一个X染成B或W中的一个。
输入格式 2237.in
第一行两个整数N和K。
第二行一个长度为N的字符串。
输出格式 2237.out
一个整数表示答案。
输入样例 2237.in
5 2
XXXXX
输出样例 2237.out
4
数据规模:
对于20%的数据, N <= 20
对于50%的数据, N <= 2000
对于100%的数据, 1 <= N, K <= 1000000
【思路】
一看这题,显然后面的方案数是可以从前面以某种玄学的方式推过来的,那么考虑dp。
由于条件的限制性,我们若是一位一位作dp,是无法处理有连续一段长度为k这个东西的。这样一来,我们就要考虑多开一维记录是否有连续一段B/W长度为k,不难发现,需要记录的实际上只有3种情况——BW都不满足,B满足,BW都满足。
好的,接下来分析如何判断是否能满足连续一段都为B/W。
其实挺容易想到的,就是利用前缀和记录前面一共有几个B/W(用hw和hb记录吧),例如我们要判断B,那么我们只需要判断hw[i]与hw[i-k]是不是相等即可。
接下来就是转移了啊~~~
用f[i][j][k]表示到了第i位,状态是j(即0是没有连续段长为k的,1是B有W没 有,2是BW都有),k是这一位选了什么(0是B,1是W)
保证连续的B后面一定有一个W,连续的W后有一个B,答案就是f[n+1][2][0]
假设这一位选B有
f[i][j][0]=f[i-1][j][1]+f[i-1][j][0]
if(i-k+1到i没有W)
f[i][1][0]=f[i][1][0]+f[i-k][0][1];
然而这样会有重复,由于f[i][0][0]是乱转移,中间有可能已经有B了,所以:
if(i-k+1到i没有W)f[i][0][0]=f[i][0][0]-f[i-k][0][1];
这样就可以了??是吧。。。
#include
#include
#include
#include
using namespace std;
const int mod=1e9+7;
const int MAXN=1e6+10;
int f[MAXN][3][3];//the ith,the state,this one choice
//state 0=none,1=have k B,2=have k W
int hb[MAXN],hw[MAXN];
int n,k;
char st[MAXN];
/*void debug()
{
for(int i=1;i<=n;++i)
{
for(int j=0;j<=2;++j)
{
printf("%d ",f[i][j][0]);
}
printf("\n");
}
printf("\n");
for(int i=1;i<=n;++i)
{
for(int j=0;j<=2;++j)
{
printf("%d ",f[i][j][1]);
}
printf("\n");
}
printf("\n");
}*/
void init()
{
scanf("%d%d",&n,&k);
scanf("%s",st+1);
st[++n]='X';
for(int i=1;i<=n;++i)
{
hb[i]=hb[i-1];hw[i]=hw[i-1];
if(st[i]=='B')
hb[i]++;
if(st[i]=='W')
hw[i]++;
}
}
void solve()
{
f[0][0][1]=1;
for(int i=1;i<=n;++i)
{
if(st[i]=='B')
for(int j=0;j<3;++j)
f[i][j][0]=(f[i-1][j][0]+f[i-1][j][1])%mod;
else
if(st[i]=='W')
for(int j=0;j<3;++j)
f[i][j][1]=(f[i-1][j][0]+f[i-1][j][1])%mod;
else
for(int j=0;j<3;++j)
{
f[i][j][0]=(f[i-1][j][0]+f[i-1][j][1])%mod;
f[i][j][1]=(f[i-1][j][0]+f[i-1][j][1])%mod;
}
if(icontinue;
if(st[i]=='B' || st[i]=='X')
{
if(hw[i]==hw[i-k])
{
f[i][1][0]=f[i][1][0]+f[i-k][0][1];
f[i][1][0]%=mod;
f[i][0][0]=f[i][0][0]-f[i-k][0][1]+mod;
f[i][0][0]%=mod;
}
}
if(st[i]=='W' || st[i]=='X')
{
if(hb[i]==hb[i-k])
{
f[i][2][1]=f[i][2][1]+f[i-k][1][0];
f[i][2][1]%=mod;
f[i][1][1]=f[i][1][1]-f[i-k][1][0]+mod;
f[i][1][1]%=mod;
}
}
}
// debug();
printf("%d\n",f[n][2][0]);
}
int main()
{
freopen("2237.in","r",stdin);
freopen("2237.out","w",stdout);
init();
solve();
return 0;
}
============我是分割线============
【原题】BZOJ 3620
题目描述
给出一个长度为n的字符串(均为小写字母),以及一个整数k。
问该字符串的所有子串中,能被切分为A+B+A的形式且 len(A) >= k, len(B) >= 1 的子串数量有多少。
输入格式 2240.in
第一行一个字符串
第二行一个整数k
输出格式 2240.out
一个整数表示答案。
输入样例 2240.in
abcabcabc
2
输出样例 2240.out
8
数据规模:
对于30%的数据: 1 <= n <= 100
对于100%的数据: 1 <= n <= 10000, 1 <= k <= 100
n<=10000 显然是直接给你暴力水的- -
看到字符串匹配,立马就有什么AC自动机之类的东西出来。
于是我们思路清晰,用KMP来暴力
枚举子串的左端点,然后枚举右端点
对于每个子串S我们要判定是否存在一个长度在[k,(|S|-1)>>1]之间的前缀与后缀匹配
那我们就求出长度不超过(|S|-1)>>1的最长前后缀,判断是否>=k即可
#include
#include
#include
#include
using namespace std;
const int MAXN=10010;
int nex[MAXN];
char s[MAXN];
int k,n,ans;
int main()
{
freopen("2240.in","r",stdin);
freopen("2240.out","w",stdout);
scanf("%s",&s);
scanf("%d",&k);
n=strlen(s);
ans=0;
for (int i=1;i<=n-k;i++)
{
nex[0]=nex[1]=0;
int x=0;
// printf("0 0 ");
for (int j=2; j<=n-i+1; j++)
{
while(x && s[i-1+x]!=s[i+j-2])
x=nex[x];
if (s[i+x-1]==s[i+j-2])
x++;
nex[j]=x;
// printf("%d ",nex[j]);
}
// printf("\n");
int tmp=ans;
x=0;
for (int j=2;j<=n-i+1;j++)
{
while(x && s[i-1+x]!=s[i+j-2])
x=nex[x];
if (s[i-1+x]==s[i+j-2])
x++;
while((x<<1|1)>j)
x=nex[x];
if (x>=k)
ans++;
}
/* printf("!!!%d\n",ans-tmp);
for(int j=0;j<=n;++j)
printf("%d ",nex[j]);
printf("\n");*/
}
printf("%d\n",ans);
return 0;
}
==========凑热闹的分割线===========
【原题】codeforces 869C
【题目大意】
有三个集合,分别含有a、b、c个点,要求给这些点连线,也可以全都不连,要求连接后,每两点距离为1,在同一集合的两点最短距离至少为3,问多少种不同的连接方案。
【思路】
首先我们简化问题,思考集合两两之间的关系。我们发现,若每两个集合都保证满足条件,那最后结果一定满足条件。
那么问题就变得比较简单了,两个集合间若要最短距离至少为3,那每个集合中的点只能同时与另一个集合中的一个点相连。
那么不难得出,若两个集合间需要连k条线,则可以在集合A中选k个点,在集合B中选k个点,共有k!种连接方式,即
C[A][k]*C[B][k]*k!。
两个集合间最少可连0条,最多可连min(A,B),因此k的范围为0~min(A,B)。
这是组合数的思考方式。
然后我们还可以引申一下,用个dp来搞搞这个东西
我们发现每个点其实就是选和不选的区别,所以
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*j;
就这样。
#include
#include
#include
using namespace std;
typedef long long LL;
const int mod=998244353;
const int MAXN=5005;
int a,b,c;
LL ans,dp[MAXN][MAXN];
void init()
{
dp[0][0]=1;
for(int i=0;i<=5000;++i)
dp[0][i]=dp[i][0]=1;
for(int i=1;i<=5000;++i)
for(int j=1;j<=5000;++j)
dp[i][j]=(dp[i-1][j]+dp[i-1][j-1]*1ll*j)%mod;
}
void solve()
{
scanf("%d%d%d",&a,&b,&c);
ans=(dp[a][b]*dp[b][c]%mod)*dp[c][a]%mod;
printf("%I64d\n",ans);
}
int main()
{
// freopen("C.in","r",stdin);
// freopen("C.out","w",stdout);
init();
solve();
return 0;
}
说实话也并不是很想打其他了啊www
那就到这里结束吧~~
撒花~~