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 ",(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 "); return ; } printf("Yes "); } 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 "); else printf("Yes "); } 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 "); else printf("Yes "); } 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 ",( (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 */