8.17暑假集训5
下发文件和题解
A.星际旅行
既然要求经过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,左右情况加起来得到: .
注意到更新 时,路径条数最多减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;
}
--END--
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/16595748.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!