T1:

分析:
定义f[i][0/1]为递推到第i位,是奇数项大于两边还是偶数项大于两边。
但是发现,上面两种情况是等价的,只需要求出一种即可,然后将答案*2。
假设已经放了i-1个合法的高度,现在将第i个高度放入。
在i-1个高度中有i个位置是可以插入的,枚举这i个位置。
假设现在放在第j个位置,那么它将序列划分成了两部分,左边有j-1个,右边有i-j个,合法方案就是f[j-1]*f[i-j]。
但这还不够,我们发现左右分别放哪些数还不知道,所以还要乘C(i-1,j-1)(在i-1个数中选j-1个放左边)
注意:模数非质数,不能用费马小定理预处理阶层,要用杨辉三角形递推!!

#include<bits/stdc++.h>
using namespace std;
#define N 4205
#define ll long long
#define ri register int
ll f[N][3],mod;
int c[N][N];
int n;
void init()
{
for(ri i=0;i<=n;++i) c[i][i]=1,c[i][0]=1;
for(ri i=1;i<=n;++i)
for(ri j=1;j<=n;++j){
c[i][j]=(c[i-1][j-1]+c[i-1][j]) %mod;
}
}
int main()
{
//freopen("rabbit.in","r",stdin);
//freopen("rabbit.out","w",stdout);
scanf("%d%lld",&n,&mod);
init();
f[0][0]=1; f[0][1]=1; f[1][1]=1; f[1][0]=1; f[2][0]=1; f[2][1]=1;
for(ri i=3;i<=n;++i){
for(ri j=1;j<=i;++j){
if(j&1) f[i][1]=( f[i][1] + f[j-1][1] * f[i-j][1] %mod *1ll*c[i-1][j-1] %mod ) %mod ;
else f[i][0]=( f[i][0] + f[j-1][0] * f[i-j][0] %mod *1ll*c[i-1][j-1] %mod ) %mod ;
}
}
printf("%lld\n",(f[n][0] + f[n][1]) %mod);
}
/*
3 107
4 107
4 1000000007
12 345
9 233
233 666
*/
T2:

分析:
这道题真的非常的水。。多画图分析一下,会发现因为字符的位置是可以随意变换的,只需要统计同种的个数,讨论n,m的奇偶性即可。

#include<bits/stdc++.h>
using namespace std;
#define ri register int
int cnt[105],n,m,T;
char s[205];
void work1()
{
int ans=0;
for(ri i=1;i<=26;++i)
if(cnt[i]%4!=0) { printf("No\n"); return ; }
printf("Yes\n");
}
void work2(int x)
{
int cnt1=0,cnt2=0,cnt4=0;
for(ri i=1;i<=26;++i)
if(cnt[i]%4==0) cnt4++;
else if(cnt[i]%2==0) cnt2++;
else cnt1++;
if(cnt1>1 || cnt2>x) printf("No\n");
else printf("Yes\n");
}
void work3(int x)
{
int cnt2=0,cnt1=0;
for(ri i=1;i<=26;++i)
if(cnt[i]%2==0 && cnt[i]%4!=0) cnt2++;
else if(cnt[i]&1) cnt1++;
if(cnt2>x || cnt1) printf("No\n");
else printf("Yes\n");
}
int main()
{
freopen("quilt.in","r",stdin);
freopen("quilt.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
memset(cnt,0,sizeof(cnt));
for(ri i=1;i<=n;++i){
scanf("%s",s);
for(ri j=0;j<m;++j) cnt[s[j]-'a'+1]++;
}
if(n%2==0 && m%2==0) work1();
else if((n&1) && (m&1)) work2( max( (n-1)/2,(m-1)/2 ) );//
else work3((n&1) ? m/2 : n/2);
}
}
/*
5
3 4
aabb
aabb
aacc
2 2
aa
bb
5 1
t
w
e
e
t
2 5
abxba
abyba
1 1
z
*/
T3:
一句话题意:根节点可以覆盖d的范围,求两两叶子结点会出发一只蚊子,遇到被覆盖的点就有p/q的概率被杀死,求被杀死的期望蚊子数。
分析:
如果是直接统计的期望死亡的蚊子数,会发现对于一个没有被覆盖的点,它子树中期望死亡蚊子数为0,不好转移。
所以补集转换,转换成求期望存活蚊子数,最后用总数减去即可。
令k=1-p/q,sum[u]为u子树中期望存活的蚊子数。
转移分两类:
1.u没被覆盖:
ans+=sum[u]*sum[v](点对数相乘即为答案,注意ans要在sum前面统计)
sum[u]+=sum[v](将v合并入了u)
2.u被覆盖了:
ans+=sum[u]*sum[v](这里为什么不再乘一个k呢,因为经过u的生存概率已经在sum[u]中被统计了,所以不在重复统计)
sum[u]+=sum[v]*k(v子树内的叶子点因为经过了u导致生存概率再乘一个k)
注意叶子结点被覆盖了应该初始化为k。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ri register int
#define N 5000005
const ll mod=1e9+7;
ll ans=0,p,q,k,m=0,sum[N];
int can[N],dep[N],d;
vector<int> e[N];
int read()
{
int x=0,fl=1; char ch=getchar();
while(ch<'0' || ch>'9') { if(ch=='-') fl=-1; ch=getchar(); }
while(ch<='9' && ch>='0') x=x*10+ch-'0',ch=getchar();
return x*fl;
}
ll quick_pow(ll a,ll k)
{
ll ans=1;
while(k){ if(k&1) ans=ans*a %mod; a=a*a %mod; k>>=1; }
return ans;
}
void dfs2(int u,int ff)//只能写一个dfs 否则会TLE
{
int fl=0;
for(ri i=0;i<e[u].size();++i){
int v=e[u][i];
if(v==ff) continue;
fl=1; dep[v]=dep[u]+1;
if(dep[v]<=d) can[v]=1;
dfs2(v,u);
ans=(ans + sum[u]*sum[v] %mod);
if(ans>=mod) ans%=mod;
if(can[u]){
sum[u]=(sum[u]+sum[v]*k %mod);
if(sum[u]>=mod) sum[u]%=mod;
}
else sum[u]=(sum[u]+sum[v]) %mod;
}
if(can[u] && !fl) sum[u]=k;
else if(!fl) sum[u]=1;
m+=(fl==0);
}
int main()
{
freopen("mosquito.in","r",stdin);
freopen("mosquito.out","w",stdout);
int n=read(); int a,b;
for(ri i=1;i<=n-1;++i) a=read(),b=read(),e[a].push_back(b),e[b].push_back(a);
d=read(); p=read(); q=read();
k=(q-p)*quick_pow(q,mod-2) %mod;
can[1]=1; dfs2(1,0);
printf("%lld\n",( (m*(m-1)-2*ans) %mod +mod )%mod);
}
/*
3
1 2
1 3
1 1 2
11
1 2
1 3
2 4
2 5
3 6
4 7
4 8
4 9
5 10
7 11
2 1 2
*/
