翻译
Sereja在平面上画了n个点,第i点(1<=i<=n)坐标为(i,0)。然后Seraja在每个点上标记了一个大写或小写的英文字母。Seraja不喜欢字母’x’,所以她没有用’x’标记任何点。Seraja认为,当标记方式满足以下条件时,这些点就被“美丽地”标记了:
1.所有点能够被分成若干点对,每个点在且仅在一个点对中; 2.在每个点对中,横坐标较小的点被小写字母标记,横坐标较大的点被同一字母的大写形式标记; 3.以每组点对连线为对角线作正方形,所有作出的正方形的边没有交点且没有顶点重合;
小Petya擦去了标记在点上的一些小写或大写字母。现在Seraja想知道有几种方式标记被擦去字母的点,能够使所有点被“美丽地”标记了。
题解
诠释 暴力踩标算, N 2 N^2 N2过百万
实际上就是给你一些左括号,右括号全部擦去的序列
有25种括号,要求你恢复这个序列
枚举左括号的话是妥妥 N 2 N^2 N2的
换种思路
设 f [ i ] [ j ] f[i][j] f[i][j]表示前i个字符填了j个右括号
显然j的上界是i/2
设已经找到了k个左括号
下界是k
只需要用是问号的部分更新
填左括号, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i−1][j]
填右括号, f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j]=f[i-1][j-1] f[i][j]=f[i−1][j−1]
25我们最后再乘
这样就能过了…
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline void pr1(int x){write(x);printf(" ");}
inline void pr2(int x){write(x);puts("");}
int n;
char ch[100005];
unsigned int f[2][50005];
int main()
{
n=read();if(n&1)return puts("0"),0;
scanf("%s",ch+1);
int now=0;f[now][0]=1;int sum=0;
for(int i=1;i<=n;i++)
{
if(ch[i]=='?')
{
now^=1;
for(int j=0;2*j<=i;j++)f[now][j]=f[now^1][j]+f[now^1][j-1];
}
else sum++;
}
unsigned int ans=f[now][n/2];
for(int i=1;i<=(n-2*sum)/2;i++)ans*=25;
printf("%u\n",ans);
return 0;
}