AT3728 Squirrel Migration

本文深入探讨了AT3728 SquirrelMigration问题的解决策略,通过构造方法实现点的匹配,并利用容斥原理进行方案数的计算。文章详细解析了如何基于树的特性,确定重心位置并进行有效的匹配,同时通过DP算法优化计算复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AT3728 Squirrel Migration 

就是给每个点分配两个匹配点(自环除外)

考虑最大值

考虑极限情况:每个边的贡献是min(sz[u],sz[v])*2

证明存在方案:

发现,如果哪边sz更小,就把这些边都往外连

这样,在重心的位置,会两两匹配闭合。

所以存在构造方案。

 

方案数?就是最后匹配的方案

重心两个:((n/2)!)^2

重心一个:

也就是,以重心为根,每个子树是一个组,每个组必须匹配别的组

而重心自己可以连自环或者匹配任意一个组

枚举重心连自环与否,做两遍。

 

现在有若干个组,每个组有num[i]个元素,每个组不能匹配自己的元素

考虑容斥!

f[i][j]前i个组,有j个位置连了自己组的元素的方案数

f[i][j+k]+=f[i-1][j]*C(num[i],k)*A(num[i],k)

看似O(n^3),实际和树形背包的sz优化一样,就是O(n^2)的

 

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}
namespace Modulo{
const int mod=1e9+7;
int ad(int x,int y){return (x+y)>=mod?x+y-mod:x+y;}
void inc(int &x,int y){x=ad(x,y);}
int mul(int x,int y){return (ll)x*y%mod;}
void inc2(int &x,int y){x=mul(x,y);}
int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
}
using namespace Modulo;
namespace Miracle{
const int N=5005;
int n;
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
int jie[N],inv[N];
int C(int n,int m){
    if(n<0||m<0||n<m) return 0;
    return mul(jie[n],mul(inv[m],inv[n-m]));
}
int A(int n,int m){
    return mul(C(n,m),jie[m]);
}
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
int be[N],sz[N],num[N],tot;
int rt[N];
void dfs(int x,int fa){
    sz[x]=1;
    int mx=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x);
        sz[x]+=sz[y];
        if(sz[y]>mx) mx=sz[y];
    }
    mx=max(mx,n-sz[x]);
    if(mx<=n/2){
        rt[++rt[0]]=x;
    }
}
void fin(int x,int fa){
    be[x]=tot;
    ++num[be[x]];
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        if(x==rt[1]){
            ++tot;
        }
        fin(y,x);
    }
}
int f[N][N];
int calc(){
    memset(f,0,sizeof f);
    f[0][0]=1;
    int sum=0;
    for(reg i=1;i<=tot;++i){
        // cout<<" i "<<i<<" "<<num[i]<<endl;
        for(reg j=0;j<=sum;++j){
            for(reg k=0;k<=num[i];++k){
                // cout<<" con "<<mul(f[i-1][j],mul(C(num[i],k),A(num[i],k)))<<endl;
                f[i][j+k]=ad(f[i][j+k],mul(f[i-1][j],mul(C(num[i],k),A(num[i],k))));
            }
        }
        sum+=num[i];
    }
    ll ret=0;
    for(reg j=0;j<=sum;++j){
        // cout<<f[tot][j]<<endl;
        if(j&1){
            ret=ad(ret,mod-mul(f[tot][j],jie[sum-j]));
        }else{
            ret=ad(ret,mul(f[tot][j],jie[sum-j]));
        }
    }
    return ret;
}
int main(){
    rd(n);
    jie[0]=1;
    for(reg i=1;i<=n;++i) jie[i]=mul(jie[i-1],i);
    inv[n]=qm(jie[n]);
    for(reg i=n-1;i>=0;--i) inv[i]=mul(inv[i+1],i+1);
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    if(rt[0]==2){
        ll ans=qm(jie[n/2],2);
        ot(ans);return 0;
    }
    // cout<<rt[1]<<endl;
    fin(rt[1],0);
    // cout<<" tot "<<tot<<endl;
    // prt(be,1,n);
    int ans=calc();
    // cout<<" ans1 "<<ans<<endl;
    num[++tot]=1;
    ans=ad(ans,calc());
    ot(ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/

构造最大值,考虑上界,再证明能不能达到。

然后构造方案很明确了,方案数就是分组,容斥DP来处理

转载于:https://www.cnblogs.com/Miracevin/p/10972371.html

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值