机房模拟测试4:计数类dp+水题+树上计数

T1:

 原题:P2467 [SDOI2010]地精部落

分析:

定义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
*/
View Code

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
*/
View Code

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
*/
View Code
原文地址:https://www.cnblogs.com/mowanying/p/11755450.html