题意

有 $n$ 个变量,第 $i$ 个变量 $x_i$ 初始值是 $a_i$,让它加一的代价是 $b_i$。每次操作选择一个变量加一。问最少需要多少代价能对于任意的 $2^k \leq i \leq n$,都有:

$$ \sum_{b=0}^k a_{\lfloor \frac{i}{2^b}\rfloor} \equiv 0 \pmod m $$

$1 \leq T \leq 10,1 \leq k \leq 10,1 \leq m \leq 200$。

题解

首先看到 $\lfloor \frac{x}{2^i} \rfloor$ 这种神奇结构直接想到二叉树:定义 $i$ 的父亲为 $\lfloor \frac{i}{2} \rfloor$。限制变为:对于二叉树上任意一个长度为 $k$ 的父子链,都要求和是 $m$ 的倍数。

那么我们发现,对于编号 $x \geq 2^k$ 的节点,都要求和 $\lfloor \frac{x}{2^k}\rfloor$ 同余,所以我们只需要考虑前 $2^k-1$ 个点。预处理出 $w_{i,j}$ 表示 $i$ 这个点,改成同余 $j$ 的代价是什么。

这个预处理其实就是要求对于每个在外面的点,要求从 $a_i$ 开始,循环加上一个等差数列,这操作要求 $O(1)$ 。可以通过差分打两次标记计算,也可以对于每个 $<2^k$ 的点,开一个 $s_{i,j}$ 表示要求和点 $i$ 相同的所有点中,$a_i \equiv j \pmod m$ 的所有点的 $b_i$ 的和。然后类似暴力枚举计算。这一部分预处理是 $O(n+2^km)$ 或 $O(n+2^km^2)$ 的。

接下来就简单了:限制只有在儿子上。设 $f_{v,i}$ 表示满足了 $v$ 子树内所有叶子的限制,当前 $v$ 到根(不包括 $v$)的权值和是 $i$ 的方案数,转移枚举一下 $v$ 填什么就好。复杂度 $O(2^km^2)$。

代码

#include <bits/stdc++.h>

#define fi first
#define se second
#define DB double
#define U unsigned
#define P std::pair
#define LL long long
#define LD long double
#define pb emplace_back
#define MP std::make_pair
#define SZ(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

inline char nc(){
    #define SIZE 1000003
    static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
    if(p1 == p2){
        p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
        if(p1 == p2) return -1;
    }
    return *p1++;
}

template <typename T>
inline void read(T &x){
    x = 0;char ch = nc();bool flag = 0;
    while(!isdigit(ch)){
        if(ch == '-') flag = 1;
        ch = nc();
    }
    while(isdigit(ch)){
        x = (x<<1)+(x<<3)+(ch^'0');
        ch = nc();
    }
    if(flag) x = -x;
}

unsigned int SA, SB, SC; int p, A, B;
unsigned int rng61(){
    SA ^= SA << 16;
    SA ^= SA >> 5;
    SA ^= SA << 1;
    unsigned int t = SA;
    SA = SB;
    SB = SC;
    SC ^= t ^ SA;
    return SC;
}

int n,m,k;
const int MAXN = 2048 + 5;
const int MAXM = 1e7 + 5;
int ta[MAXM],tb[MAXM],mx[MAXN];
LL w[MAXN][205],s[MAXN][205];
LL f[MAXN][205];

void gen(){
    read(n);read(k);read(m);read(p);read(SA);read(SB);read(SC);read(A);read(B);
    ++k;
    FOR(i,1,p) read(ta[i]),read(tb[i]);
    FOR(i,p+1,n) ta[i] = rng61()%A+1,tb[i] = rng61()%B+1;
    FOR(i,1,(1<<k)-1) FOR(j,0,m-1) w[i][j] = s[i][j] = 0;
    FOR(i,1,n){
        int x = i;while(x >= (1<<k)) x >>= k;
        s[x][ta[i]%m] += tb[i];
    }
    FOR(i,1,(1<<k)-1){
        FOR(j,0,m-1){
            FOR(k,0,m-1){
                w[i][j] += 1ll*s[i][k]*((j-k+m)%m);
            }
        }
    }
}

inline void dfs(int v){
    FOR(i,0,m-1) f[v][i] = 1e18;
    if((v<<1) >= (1<<k)){
        FOR(i,0,m-1) f[v][(m-i)%m] = w[v][i];
        return;
    }
    dfs(v<<1);dfs(v<<1|1);
    FOR(i,0,m-1){
        FOR(j,0,m-1){
            f[v][i] = std::min(f[v][i],f[v<<1][(i+j)%m]+f[v<<1|1][(i+j)%m]+w[v][j]);
        }
    }
}

inline void Solve(){
    gen();
    dfs(1);
    printf("%lld\n",f[1][0]);
}

int main(){
    int T;read(T);
    while(T--) Solve();
    return 0;
}
Last modification:May 14th, 2021 at 08:59 pm
如果觉得我的文章对你有用,请随意赞赏