2019.07.18NOIP模拟5

真TM不容易,LZ终于有分啦!!!撒花~~~

T1 星际旅行

按说我是不会这个题的,然而我一不小心拿了80分QAQ

感jio这个solution讲的还是挺明确的:

将所有的边拆成两条,问题变成去掉两条边,使得原图存在一条欧拉路径。

注意新图中所有点的度数均为偶数,只需按照去掉任意2个自环、去掉任意1个自环和任意一条边、去掉两条有公共顶点的边进行讨论即可。

注意图不连通的判断方式,不是点不连通,而是边不连通。

 

注意的都挺对的,判断边联通我用的是dfs,vis数组标记就好了

跑dfs的时候,如果当前点有出度,但是其中有的边没有访问过,直接输出0就好了

或者该图不是边的联通图,直接输出0就行

然而数据不知道为啥不用进行此判断就80。。。

但是满分的话,一定要判断才能过

 

分出两种情况:

1.没有自环

我们从某一点出发,根据题义把每一条边改成双向的,按照欧拉回路的性质,最终一定会回到这个点。

2.有自环

加入自环,将自环分离出来,若有一个自环,则其贡献方案数为非自环边数与总自环数-1(减去本身),即经过此点时走一次自环,再枚举剩下的任意一条边(包括自环),即从那条边任意端点往外出发,经过此自环时走一次的路径。

需要统计出自环的边数和非自环的边数,更新答案用

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>
#define int long long
using namespace std;

inline int read(){
    int x=0,w=1;
    char ch=getchar();
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*w;
}

const int maxn=500005;
vector<int> G[maxn];
int ans,n,m;
int u,v;
int cnt1,cnt2;//非,是
int vis[maxn];

inline void dfs(int u,int fa){
    vis[u]=1;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==fa) continue;
        if(vis[v]) continue;
        dfs(v,u);
    }
}

int flag[maxn];
signed main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        u=read(),v=read();
        if(u!=v){
            G[u].push_back(v);
            G[v].push_back(u);
            cnt1++;//
        }
        else cnt2++,flag[u]=1;//
    }
    for(int i=1;i<=n;i++)
        if(G[i].size()){
            dfs(i,0);
            break;
        }
    for(int i=1;i<=n;i++)
        if(G[i].size()&&!vis[i])
            return cout<<0<<endl,0;
    for(int i=1;i<=n;i++)
        if(!G[i].size()&&flag[i])
            return cout<<0<<endl,0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<G[i].size();j++){
            int v=G[i][j];
            ans+=G[v].size()-1;
        }
    ans+=cnt1*cnt2*2+cnt2*(cnt2-1);
    printf("%lld\n",ans/2);
    return 0;
}    
Code

 T2 砍树

砍树难,难于上青天~~~

传送门

T3 超级树

这场模拟赛跟“树”刚上了

这道题是个神仙DP

 记f[i][j]表示二叉树的深度为i时,能找出j条互不相交的道路的方案数

由此可见,当i变成i+1是,就意味着,两棵深度为i的树连上了一个根,然后将所有点和跟节点连起来

记状态f[i][l]表示深度为i+1的超级树的左子树中选出l条不相交的路径,f[i][r]表示深度为i+1的右子树中选出r条不相交的路径的方案数

然后用到分类讨论的思想,从不同情况转移出f[i+1]根节点不参与转移

  • 根节点不参与转移          dp[i+1][l+r]=dp[i][l]*dp[i][r]
  • 根节点一个点作为一条新的路径         dp[i+1][l+r+1]+=dp[i][l]*dp[i][r]
  • 根连接到左子树(或右子树)的某条路径上         dp[i+1][l+r]+=2*dp[i][l]*dp[i][r]*(l+r)
  • 根连接左子树和右子树的各一条路径           dp[i+1][l+r-1]+=2*dp[i][l]*dp[i][r]*l*r
  • 根连接左子树(或右子树)的两条路径           dp[i+1][l+r-1]+=dp[i][l]*dp[i][r]*(l*(l-1)+r*(r-1))

对于边界问题,稍加思索就可以确定

dp[1][0]=dp[1][1]=1

答案保存在dp[n][1]里面,意味着在深度为n的树上找一条互不相交道路的方案数

复杂度$ O(k^3) $

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;

inline int read(){
    int x=0,w=1;
    char ch=getchar();
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*w;
}

int n,mod;
const int maxn=305;
int f[maxn][maxn];
int sum;

signed main(){
    n=read(),mod=read();
    f[1][0]=f[1][1]=1;
    for(int i=1;i<=n;i++){
        for(int k=1;k<=n;k++)
            f[i][k]%=mod;
        for(int l=0;l<=n;l++)
            for(int r=0;r+l<=n;r++){
                sum=f[i][l]*f[i][r]%mod;
                f[i+1][l+r]+=sum;
                f[i+1][l+r+1]+=sum;
                f[i+1][l+r]+=2*sum*(l+r);
                f[i+1][l+r-1]+=2*sum*l*r;
                f[i+1][l+r-1]+=sum*(l*(l-1)+r*(r-1));
            }
    }
    cout<<f[n][1]%mod;
    return 0;
}
Code

 

 

 明天模拟赛RP++

转载于:https://www.cnblogs.com/rui-4825/p/11206057.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值