P7718 「EZEC-10」Equalization 题解

题目链接

点击打开链接

题目解法

牛牛牛

显然转化成差分数组做,令 \(b_i=a_{i+1}-a_i\)
考虑不同操作给 \(b\) 带来的影响

  1. \(l=1,r=n\),没影响
  2. \(l=1,r<n\)\(b_r:=b_r+x\)
  3. \(l>1,r=n\)\(b_{l-1}:=b_{l-1}+x\)
  4. \(l>1,r<n\)\(b_{l-1}:=b_{l-1}+x,\;b_r:=b_r-x\)

所以操作相当于单点加和两个数和相同,随便改
我们把操作看成连边,则有一个结论:
在最优策略中,对于一个连通块,令其 \(b\) 之和为 \(S\),则有:

  1. \(S=0\),连通块为一棵树
  2. \(S\neq 0\),连通块为一棵树 + 一个自环

应该可以构造性证明,我只口胡了一下,差不多就是:

  1. 对于树,从叶子往上操作,目标是使叶子为 \(0\),父亲跟着动就好了,最后整棵树都为 \(0\)
  2. 对于一个自环和树,把树根定于自环,然后做树的操作,最后对着自环做一下就好了

我们用状压 \(dp\) 做这东西,记一下当前状态的最小操作数和方案数
然后每次添加一个连通块转移即可(树的话就是 \(prufer\) 序列的 \(siz^{siz-2}\),加一个自环就 \(\times 2siz\),因为有 \([1,t],[t+1,n]\) 两种方法改变 \(t\) 处的值

时间复杂度 \(O(3^{n-1})\)

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
    FF=0;int RR=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    FF*=RR;
}
const int N=18,P=1e9+7;
int n,a[N],b[N],ppc[1<<N],wys[N],f[1<<N],g[1<<N];
LL sum[1<<N];
inline void inc(int &x,int y){ x+=y;if(x>=P) x-=P;}
#define lowbit(x) x&-x
int main(){
    read(n);
    F(i,0,n-1) read(a[i]);
    F(i,0,n-2) b[i]=a[i+1]-a[i];
    int full=(1<<(n-1))-1;
    F(S,1,full){
        ppc[S]=__builtin_popcount(S);
        F(i,0,n-2) if(S>>i&1){ sum[S]=sum[S^1<<i]+b[i];break;}
    }
    F(i,1,n-1){
        wys[i]=1;
        F(j,1,i-2) wys[i]=1ll*wys[i]*i%P;
    }
    ms(f,0x3f);
    f[0]=0,g[0]=1;
    F(S,1,full){
        int nS=S^lowbit(S);
        for(int nT=nS;;nT=(nT-1)&nS){
            int T=nT^lowbit(S);
            int nf=f[S^T]+ppc[T]-1+(sum[T]!=0),ng=1ll*g[S^T]*wys[ppc[T]]%P;
            if(sum[T]) ng=2ll*ng*ppc[T]%P;
            if(nf<f[S]) f[S]=nf,g[S]=0;
            if(nf==f[S]) inc(g[S],ng);
            if(!nT) break;
        }
    }
    F(i,1,f[full]) g[full]=1ll*g[full]*i%P;
    printf("%d\n%d\n",f[full],g[full]);
    return 0;
}

posted @ 2024-09-18 15:42  Farmer_D  阅读(22)  评论(0)    收藏  举报