CodeForces 1025G Company Acquisitions

题意

描述有点麻烦,就不写了。

( exttt{Data Range:}1leq nleq 500)

题解

势能函数这个东西好神啊……

这个题目用常规的 DP 好像做不出来,所以我们可以考虑设计一个函数将一个局面到最终局面的期望进行评估,如果这个函数能够满足任意一次操作会使得函数值的期望增加 (1),那么就可以用最终局面的函数值减去初始局面的函数值就得到期望操作次数了。

同时,必须满足最终局面无法转移,因为有环的转移图比较难去算转移,这个时候势能函数就出来了。

这个题目中我们考虑设 (F(S)=sum f(a_i)),其中 (a_i) 表示有多少个点依赖这个点(当 (i) 依赖别的点的时候 (a_i) 没有定义)。现在我们想来求出这个 (f)

考虑每一次随机选择两个点 (p,q) 来合并,(p,q) 原本有 (u,v)依赖它的点,那么根据之前的定义有如下方程:(这里应该是 ( exttt{s})( exttt{kydogli}) 写错了)

[f(u)+f(v)+1=frac{1}{2}[f(u+1)+vf(0)]+frac{1}{2}[f(v+1)+uf(0)] ]

这里有一个 trick:在势能函数中,可以直接钦定 (f(0)=0),于是得到

[f(u)+f(v)+1=frac{1}{2}left(f(u+1)+f(v+1) ight) ]

由于这个等式看起来非常对称,同时它需要对于任意 (u,v) 都成立,于是我们可以考虑分离 (u)(v)

[left(f(u)+frac{1}{2} ight)+left(f(v)+frac{1}{2} ight)=frac{1}{2}f(u+1)+frac{1}{2}f(v+1) ]

所以很明显有

[2f(u)+1=f(u+1) ]

这个东西跟 Hanoi 塔的递推式是一样的,所以我们可以直接写出 (f(u)=2^u-1)

然后用末状态的势能函数值减去初始状态的就得到了期望操作次数。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2e5+51,MOD=1e9+7;
ll n,x,res;
ll sz[MAXN];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
inline ll qpow(ll base,ll exponent)
{
    ll res=1;
    while(exponent)
    {
        if(exponent&1)
        {
            res=(li)res*base%MOD;
        }
        base=(li)base*base%MOD,exponent>>=1;
    }
    return res;
}
int main()
{
    n=read();
    for(register int i=1;i<=n;i++)
    {
        x=read(),x!=-1?sz[x]++:1;
    }
    for(register int i=1;i<=n;i++)
    {
        sz[i]?res=(res+qpow(2,sz[i])-1)%MOD:1;
    }
    printf("%d
",(qpow(2,n-1)-1-res+MOD)%MOD);
}
原文地址:https://www.cnblogs.com/Karry5307/p/13829794.html