• [Contest on 2020.11.21] 树上竞技


    ( ext{Description})

    传送门

    ( ext{Solution})

    他们通过无线通讯设备确定一个集合点,然后所有人沿最短路径走到集合点,花费的代价为所有人走的路径长度之和。 为了取得竞技的胜利,他们需要最小化花费的代价。

    其实如果是指定节点/任意节点最小化花费应该都是很好做的换根 ( ext{DP})

    然而他们并不知道他们最终会进入哪个节点,所以你需要对于每种不同的情况计算最小代价并求出总和。

    这个就很艹了,如果用普通的 (f[i][j]) 定义(枚举集合点)并不好做:算总方案数就没法保证最小化,算 (min) 就没法算总方案。

    头大。

    正解是对于每条线段来算贡献。考虑一条边两端关键点个数分别为 (x,m-x),如果有 (xle m-x),肯定会把集合点设在 (m-x) 的那个连通块里,这样它只会被经过 (x) 次。

    假设一条边两侧分别有 (s,n-s) 个点,那么这条边的贡献就是:

    [sum_{x=1}^{m-1} ext C(s,x) imes ext C(n-s,m-x) imes min(x,m-x) ]

    这样肯定过不去,考虑优化掉 (min),其实如果强制 (xle m-x) 就有 (xle lfloorfrac{m}{2} floor)。考虑 (x) 范围只有 ([1,m-1]),把偶数的 (frac{m}{2}) 情况刨开,就有,

    [sum_{x=1}^{lfloorfrac{m-1}{2} floor} ext C(s,x) imes ext C(n-s,m-x) imes x +sum_{x=1}^{lfloorfrac{m-1}{2} floor} ext C(n-s,x) imes ext C(s,m-x) imes x+[m\%2==0] ext C(s,frac{m}{2}) imes ext C(n-s,frac{m}{2}) imes frac{m}{2} ]

    那么我们只用快速求(前面两项差不多):

    [sum_{x=1}^{lfloorfrac{m-1}{2} floor} ext C(s,x) imes ext C(n-s,m-x) imes x ]

    到这里就有两种思路:将求和去掉或预处理后 (mathcal O(1)) 递推。

    可是我数学差只会看题解,题解写什么就是什么 (qwq)

    (lfloorfrac{m-1}{2} floor=k),就有:

    [s imes sum_{x=1}^{k} ext C(s-1,x-1) imes ext C(n-s,m-x) ]

    并令其为 (s imes g(s))

    这个柿子有组合意义(就是排列组合的经典模型):在 (n-1) 个球里选 (m-1) 个球,保证前 (s-1) 中最多选 (k-1) 的方案数。显然 (g(s+1)) 相对于 (g(s)) 只少了在前 (s-1) 选了 (k-1) 个球,又选了第 (s) 个球的情况(考虑限制更紧,肯定会变少),这种方案数可以表示为 ( ext C(s-1,k-1) imes ext C(n-1-s,m-1-k)):在前 (s-1) 选了 (k-1) 个球,(n-1-s) 表示除了前 (s) 个球的球数,(m-1-k) 表示前面 (s) 个球选了 (k) 个,那么后面 (n-1-s) 个球要选 (m-1-k) 个,显然这样强制了选了第 (s) 个球。

    ( ext{Code})

    #include <cstdio>
    
    #define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
    #define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
    #define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
    #define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
    #define print(x,y) write(x),putchar(y)
    
    template <class T> inline T read(const T sample) {
        T x=0; int f=1; char s;
        while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
        while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
        return x*f;
    }
    template <class T> inline void write(const T x) {
        if(x<0) return (void) (putchar('-'),write(-x));
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
    template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
    template <class T> inline T fab(const T x) {return x>0?x:-x;}
    template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
    template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
    template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}
    
    const int mod=1e9+7,maxn=1e6+5; 
    
    int n,m,fac[maxn],ifac[maxn],k,siz[maxn],ans,g[maxn];
    int head[maxn],to[maxn<<1],nxt[maxn<<1],cnt;
    
    void addEdge(int u,int v) {
    	nxt[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
    	nxt[++cnt]=head[v],to[cnt]=u,head[v]=cnt;
    }
    
    int qkpow(int x,int y) {
    	int r=1;
    	while(y) {
    		if(y&1) r=1ll*r*x%mod;
    		x=1ll*x*x%mod; y>>=1;
    	}
    	return r;
    }
    
    void init() {
    	fac[0]=1;
    	rep(i,1,n) fac[i]=1ll*fac[i-1]*i%mod;
    	ifac[n]=qkpow(fac[n],mod-2);
    	fep(i,n-1,0) ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
    }
    
    int C(int x,int y) {
    	if(x<y) return 0;
    	return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
    }
    
    void dfs(int u,int fa) {
    	siz[u]=1;
    	erep(i,u) {
    		if(v==fa) continue;
    		dfs(v,u);
    		siz[u]+=siz[v];
    		ans=((ans+g[siz[v]])%mod+g[n-siz[v]])%mod;
    		if(!(m&1)) ans=(ans+1ll*C(siz[v],m>>1)*C(n-siz[v],m>>1)%mod*(m>>1)%mod)%mod;
    	} 
    }
    
    int main() {
    //	freopen("meeting.in", "r", stdin);
    //	freopen("meeting.out", "w", stdout);
    	int x;
    	n=read(9),m=read(9); k=m-1>>1; init();
    	rep(i,2,n) x=read(9),addEdge(x,i);
    	if(k>=1) g[1]=C(n-1,m-1); // 注意特判这个时候没有数值
    	rep(i,1,n-1) g[i+1]=(g[i]-1ll*C(i-1,k-1)*C(n-1-i,m-1-k)%mod+mod)%mod;
    	rep(i,1,n) g[i]=1ll*g[i]*i%mod;
    	dfs(1,0);
    	print(ans,'
    ');
    	return 0;
    } 
    
  • 相关阅读:
    [ZJOI2006]书架
    luogu P3369 【模板】普通平衡树(splay)
    MegaCli是一款管理维护硬件RAID软件,可以通过它来了解当前raid卡的所有信息,包括 raid卡的型号,raid的阵列类型,raid 上各磁盘状态
    ipmi配置方法-20200328
    debian配置---->/etc/apt/sources.list apt基本源设置指南
    Alien 魔法:RPM 和 DEB 互转
    Debian 9 中设置网络
    SSH自动断开连接的原因-20200323
    mpstat命令
    dstat命令
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14055854.html
Copyright © 2020-2023  润新知