真TM不容易,LZ终于有分啦!!!撒花~~~
T1 星际旅行
按说我是不会这个题的,然而我一不小心拿了80分QAQ
感jio这个solution讲的还是挺明确的:
将所有的边拆成两条,问题变成去掉两条边,使得原图存在一条欧拉路径。
注意新图中所有点的度数均为偶数,只需按照去掉任意2个自环、去掉任意1个自环和任意一条边、去掉两条有公共顶点的边进行讨论即可。
注意图不连通的判断方式,不是点不连通,而是边不连通。
注意的都挺对的,判断边联通我用的是dfs,vis数组标记就好了
跑dfs的时候,如果当前点有出度,但是其中有的边没有访问过,直接输出0就好了
或者该图不是边的联通图,直接输出0就行
然而数据不知道为啥不用进行此判断就80。。。
但是满分的话,一定要判断才能过
分出两种情况:
1.没有自环
我们从某一点出发,根据题义把每一条边改成双向的,按照欧拉回路的性质,最终一定会回到这个点。
2.有自环
加入自环,将自环分离出来,若有一个自环,则其贡献方案数为非自环边数与总自环数-1(减去本身),即经过此点时走一次自环,再枚举剩下的任意一条边(包括自环),即从那条边任意端点往外出发,经过此自环时走一次的路径。
需要统计出自环的边数和非自环的边数,更新答案用
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#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; }
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) $
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#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; }
明天模拟赛RP++