返回上一页

8.17暑假集训5

下发文件和题解

A.星际旅行

既然要求经过2条边恰好一次,考虑把所有双向边,每次选取两个将其变为单向边.然后判断是否为回路.这样暴力方法就有了.

不难发现一个性质:当且仅当两条边连向同一个点时,这两条边和其余所有边组成的联通图存在欧拉路.

因此设每个点的度数为 ,那么这个点的方案数即为 .把所有点总和起来即可.        —— ①

但是题目中说有自环,发现自环删去与否不影响欧拉路.

那么分为以下两种情况:

  1. 2个自环,这种情况与上面边的相同,设自环数为 ,方案数为 ;        —— ②
  2. 一个自环一条边,这种情况删哪一条边均无影响.设自环数为 、所有非自环的边数为 ,方案数为 .        —— ③

由于 ,所以每次枚举边时就可以直接计算出相应的结果.

点击查看代码

代码有点乱,凑合看看吧^o^

#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 100001
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}
ll n,m,ans,cnt,cnt2,t;
vector<pll> g[maxn];
ll tot1,pre[maxn],du[maxn];
bool vis[maxn];
static inline void dfs(rll x)
{
	for(rll i=0;i<g[x].size();i++)
	{
		rll to=g[x][i].first;
		if(!vis[g[x][i].second]) tot1++,vis[g[x][i].second]=1,dfs(to);
	}
}
int main()
{
	n=read();m=read();
	for(rll i=1,u,v;i<=m;i++)
	{
		u=read(),v=read();
		g[u].push_back((pll) { v,i });g[v].push_back((pll) { u,i });
		if(u==v) cnt++;
		else du[u]++,du[v]++,cnt2++;
	}
	t=1;
	while(t<=n&&(!du[t])) t++;
	if(t==n+1) { puts("0"); return 0; }
	dfs(t);//判是否为边联通
	if(m!=tot1) { puts("0"); return 0; }
	ans+=(cnt*(cnt-1))>>1; // ②
	ans+=cnt*cnt2; // ③
	for(rll i=1;i<=n;i++) ans+=((du[i]*(du[i]-1))>>1); // ①
	write(ans);
	return 0;
}

B.砍树

其实把题意转化一下就是让我们求一个最大的 ,使得下式

最大化.

移项,得:

.

显然这样会超时.可以发现,对于每个 只有 种取值.所以考虑使用数论分块来解决该题,当前块处理完以后直接跳到下一个块内.因为每一个块内 的值都是相同的,所以能够遍历到所有情况.

for(rll i=1;i<=n;i++) sum+=a[i];
for(rll l=1,d,now;l<=sum;l=d+1)
{
	d=sum/(sum/l);now=0;
	for(rll i=1;i<=n;i++)
		now+=(ll)ceil((double)a[i]/d);
	if(now*d<=sum) ans=d;
}

C.超级树

dp题.设 为一棵 条点不重复的路径(只要路径经过的点集不同都叫不重复)的方案数.

这里所说的层数是指从下至上的层数,叶结点为1、根结点为k.

显然,,所求答案为 .

那么针对每一层,更新状态时枚举它的左、右子树的不同路径数 .令 ,转移时分为下面5种情况:

  1. 保留其子树原有的情况:

  2. 以根自己开辟一条新路径,将原来的路径经过的点集中加入根结点:

  3. 根连接到左(或右)子树的一条路径上,左边有 条路径且有正反两个方向,右边有 条路径且有正反两个方向:

  4. 根连接左子树和右子树的各一条路径,由于 内的边可以任意组合,且可以正反两个方向,连完后两条路径合并为一条路径,所以:

  5. 根结点连接左(或右)子树中的两条路径,每一边任意两条路径有 种选择,且为正、反两个方向,化简后即为 ,由于两条边合并边数减1,左右情况加起来得到: .

注意到更新 时,路径条数最多减1( ).所以在枚举上限时只需要将 从枚举到 即可,而并不需要枚举到 .

点击查看代码

代码有点乱,凑合看看吧^o^

#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 401
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}
ll k,mod;
ll dp[maxn][maxn];
int main()
{
	k=read();mod=read();
	dp[1][0]=dp[1][1]=1;
	for(rll i=1,num;i<k;i++)
		for(rll l=0;l<=k;l++)
			for(rll r=0;l+r<=k;r++)
			{
				num=dp[i][l]*dp[i][r]%mod;
				dp[i+1][l+r]=(dp[i+1][l+r]+num)%mod;
				dp[i+1][l+r+1]=(dp[i+1][l+r+1]+num)%mod;
				dp[i+1][l+r]=(dp[i+1][l+r]+(num<<1)%mod*(l+r)%mod)%mod;
				if(l+r) dp[i+1][l+r-1]=(dp[i+1][l+r-1]+(num<<1)%mod*l%mod*r%mod)%mod;
				if(l+r) dp[i+1][l+r-1]=(dp[i+1][l+r-1]+num*(l*(l-1)%mod+r*(r-1)%mod)%mod)%mod;
			}
	write(dp[k][1]%mod);
	return 0;
}

D.求和


签到题.每次只需要求一下所求两个点的深度以及它们的LCA的深度,两边暴力加即可.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 300001
#define mod 998244353
using namespace std;
static inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
static inline void write(rll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);putchar(x%10|48);
}
ll n,q,i,j,k,l,ans;
vector<ll> g[maxn];
ll dep[maxn],siz[maxn],son[maxn],f[maxn],dfn[maxn],top[maxn],tot;
static inline void dfs1(rll x,rll fa)
{
	f[x]=fa;dep[x]=dep[fa]+1;siz[x]=1;son[x]=0;
	for(rll i=0;i<g[x].size();i++)
	{
		rll to=g[x][i];
		if(to==fa)continue;
		dfs1(to,x);siz[x]+=siz[to];
		if(siz[son[x]]<siz[to])son[x]=to;
	}
}
static inline void dfs2(rll x,rll fa)
{
	dfn[x]=++tot;top[x]=fa;
	if(!son[x])return;dfs2(son[x],fa);
	for(rll i=0;i<g[x].size();i++)
	{
		rll to=g[x][i];
		if(to==f[x]||to==son[x]) continue;
		dfs2(to,to);
	}
}
static inline ll LCA(rll x,rll y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=f[top[x]];
	}
	if(dep[x]<dep[y]) return x;
	return y;
}
static inline ll ksm(rll a,rll b)
{
	rll ans=1;a%=mod;
	for(rll i=b;i;i>>=1)
	{
		if(i&1) ans=ans*a%mod;
		a=a*a%mod;
	}
	return ans;
}
int main()
{
	n=read();
	for(rll i=1,u,v;i<n;i++) u=read(),v=read(),g[u].push_back(v),g[v].push_back(u);
	dfs1(1,0);dfs2(1,1);
	q=read();
	while(q--)
	{
		i=read();j=read();k=read();
		l=LCA(i,j);
		ans=ksm(dep[l]-1,k);
		for(rll m=dep[l];m<=max(dep[i]-1,dep[j]-1);m++)
		{
			if(m<dep[i]&&m<dep[j]) ans=(ans+(ksm(m,k)<<1)%mod)%mod;
			else ans=(ans+ksm(m,k))%mod;
		}
		write(ans);puts("");
	}
	return 0;
}
posted @   1Liu  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示