zoukankan      html  css  js  c++  java
  • 3月28日考试 题解(二分答案+树形DP+数学(高精))

    前言:考试挂了很多分,难受……

    ---------------------

    T1:防御

    题意简述:给一条长度为$n$的序列,第$i$个数的值为$a[i]$。现让你将序列分成$m$段,且让和最小的一段尽可能得大。求这个最大值。

    -------------------------

    题意很明显,最小值最大。不难想到二分答案,为节约时间我们在$(minn,sum)$这个区间内维护。考虑用前缀和维护$sum$。(不知道为什么不用前缀和挂掉50分……。

    代码:

    //二分答案 nlogn=4e6 
    #include<bits/stdc++.h>
    #define int unsigned long long
    using namespace std;
    int n,m,a[100005],sum[100005],minn=0x3f3f3f3f;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;    
    } 
    bool check(int x)
    {
        int jishu=0,last=0;
        for (int i=1;i<=n;i++)
        {
            if (sum[i]-sum[last]>=x)
            {
                last=i;
                jishu++;
            }
            if(jishu==m) return true; 
        }
        return false;
    }
    signed main()
    {
        n=read(),m=read();
        for (int i=1;i<=n;i++)
        {
            a[i]=read();
            sum[i]=sum[i-1]+a[i];
            minn=min(minn,a[i]);
        }
        int l=minn,r=sum[n];
        while(l<=r)
        {
            int mid=(l+r)/2;
            if (check(mid)) l=mid+1;
            else r=mid-1;
        }
        printf("%ld",r);
        return 0;
    }

    T2:切树游戏

    题意简述:现在有一颗黑白树。每个节点为黑点或白点。现在让你删去一些边,使每个连通块中恰好有一个黑点。求方案数。答案对1e9+7取模。

    ------------------

    树形DP。

    我们设$f[i][0]$为以i为根的树删去一些子树后不含黑点的方案数,设$f[i][1]$为以i为根的树删去一些子树后含黑点的方案数。

    则有:

    $f[u][1]=f[u][1]*(f[v][0]+f[v][1])+f[u][0]*f[v][1]$

    $f[u][0]=f[u][0]*(f[v][0]+f[v][1])$

    先对$f[u][1]$进行解释:若当前$u$所在的连通块已含有黑点,若子树含黑点,则删去;若子树不含黑点,则连上。又根据乘法原理得第一部分式子。若当前$u$所在的节点不含黑点,那么只有当子树含黑点时,才连上。由此得出$f[u][1]=f[u][1]*(f[v][0]+f[v][1])+f[u][0]*f[v][1]$。$f[u][0]$同理。

    注意两条方程不能调换顺序,因为$f[u][1]$要用到上一阶段$f[i][0]$的值。

    对于树形dp我们用dfs递归实现。

    代码:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=2e5+5;
    const int MOD=1e9+7;
    int head[maxn*2],cnt,f[maxn][2];
    int a[maxn],n;
    struct node
    {
        int next,to;
    }edge[maxn];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;    
    }
    void add(int from,int to)
    {
        edge[++cnt].next=head[from];
        edge[cnt].to=to;
        head[from]=cnt;
    }
    void dfs(int u,int fa)
    {
        for(int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==fa) continue;
            dfs(v,u);
            f[u][1]=(f[u][1]*(f[v][0]+f[v][1])%MOD+f[u][0]*f[v][1]%MOD)%MOD;
            f[u][0]=(f[u][0]*(f[v][0]+f[v][1]))%MOD;
        }
    }
    signed main()
    {
        n=read();
        for (int i=2;i<=n;i++)
        {
            int p=read();p++;
            add(i,p);add(p,i);
        }
        for (int i=1;i<=n;i++) a[i]=read(),f[i][a[i]]=1;
        dfs(1,0);
        printf("%ld",f[1][1]);
        return 0;
    }

    T3;二叉树

    题意简述:现在给你含有$n$个节点的二叉树,问组成不同二叉树的方案数。每个点认为是互不相同的。$nleq 10000$

    -----------------

    假设我们已经将一个节点定为根。

    那么子树组成方案为$f[0] imes f[i-1]+f[1] imes f[i-2]+…+f[i-1] imes f[0]$。

    符合卡特兰数的定义。

    因为n的数据范围较大,要用到高精度。(卡特兰数有通项公式,但我并不会高精度除法QAQ。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int a[10005][1000005];
    int c[1000005];
    void cheng(int f,int h)
    {
        for (int i=1;i<=a[f][0];i++)
        {
            int x=0;
            for (int j=1;j<=a[h][0];j++)
            {
                c[i+j-1]=a[f][i]*a[h][j]+x+c[i+j-1];
                x=c[i+j-1]/10;
                c[i+j-1]%=10;
            }
            c[i+a[h][0]]=x;
        }
        int lenc=100000;
        while(!c[lenc]) lenc--;
        c[0]=lenc;
    }
    void jia(int f)
    {
        int x=0;
        int len=1;
        while(len<=c[0]||len<=a[f][0])
        {
            a[f][len]+=c[len]+x;
            x=a[f][len]/10;
            a[f][len]%=10;
            len++;
        }
        a[f][len]=x;
        if (x) a[f][0]+=len;
        else a[f][0]+=len-1;
    }
    int main()
    {
        int n;cin>>n;
        a[1][0]=1;a[1][1]=1;
        a[0][1]=1;a[0][0]=1;
        for (int i=2;i<=n;i++)
        {
            for (int j=0;j<=i-1;j++)
            {
                memset(c,0,sizeof(c));
                cheng(j,i-1-j);
                jia(i);
            }
        }
        int len=100000;
        while(!a[n][len]) len--;
        for (int i=len;i>=1;i--) printf("%d",a[n][i]);
        return 0;
    }
  • 相关阅读:
    洛谷 P1219 八皇后【经典DFS,温习搜索】
    洛谷 P1972 [SDOI2009]HH的项链【莫队算法学习】
    hihoCoder #1015 : KMP算法【KMP裸题,板子】
    UVa 10341
    UVa 11461
    Uva
    BZOJ 3097: Hash Killer I【构造题,思维题】
    BZOJ 1207: [HNOI2004]打鼹鼠【妥妥的n^2爆搜,dp】
    BZOJ 1800: [Ahoi2009]fly 飞行棋【思维题,n^4大暴力】
    新版百度指数2013-12-23正式上线
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/12590173.html
Copyright © 2011-2022 走看看