题解
感觉这种分析性质的题目我搞不动啊
花了好久才弄得大致明白了
首先可以看出这个限制使得(n)个点构成了一棵满二叉树
并且树上每条长度为((k+1))的自上往下的链都是(m)的倍数
所以我们可以想到每个点的(a)都对(m)取mod
我们可以发现第(t)层的物品的(a)一定与第(t+k+1)层的物品同余
所以可以仅计算出上面的(k+1)层每个节点的(a)值,那么下面的所有点的(a)值就都是唯一确定的了
下面的节点用处不大,我们直接把这棵树弄成只有前(k+1)层节点的一棵树
这样我们可以发现一个性质,就是对于每条从根到叶子的链,链上(a)的和一定是(m)的倍数
这样节点数就最多只有(2^{k+1}=2048)个了
那么(m)也不大,我们就可以设计一个状态了:(f[i][j])表示从节点(i)出发,一路上的(a)值之和(mod m = j)的最小花费
答案显然就是(f[1][0])了
那么转移也应该是不难的
我们就枚举(j)是多少,再枚举这个节点的(a)值是多少
那么这个(dp)的转移就是(f[u][i] = min(f[u][j] , f[ls][(i-j+m)mod m] + f[rs][(i-j+m)mod m] + 这个节点的a值修改为j的代价))
那么现在的问题就只有如何处理处这个节点的a值修改为j的代价这个东西了
这玩意儿似乎不是很好求
所以我们要把ta预处理出来
设(g[u][i])表示点u修改为(i)的代价
假设我们预处理时到了节点(v)
由于我们只(dp)了前((k+1))层
所以考虑前((k+1))层的哪些点修改会对这个点造成影响?
因为第(t)层的物品的(a)一定与第(t+k+1)层的物品同余
所以就让这个节点往上若干次((k+1))步,直到跳到深度小于等于((k+1))的节点(u)
那么我们就考虑当点(u)修改为(i)时点(v)对于点(u)的贡献
我们需要分情况讨论一下:
当(i >= a[v] : g[u][i] += (i - a[v]) * b[v])
当(i < a[v] : g[u][i] += (m + i - a[v]) * b[v])
直接暴力更新的复杂度为(O(nm)),这样就可以获得(70)分了
那么把这玩意儿化开
就是(g[u][i] += i imes sum_{v in son[u] }b[v] - sum_{v in son[u]}{a[v] imes b[v]} + [i < a[v]]m imes sum_{v in son[u]}{b[v]})
前面两项就对前((k+1))的点记录一下(sum_b , sum_{a imes b})就行了
后面一项每次(O(1))打一个标记最后对于前((k+1))层的每个节点做一遍后缀和就好了
这样我们就处理出来了(g)
那么正经的(dp)式子就是(f[u][i] = min(f[u][j] , f[ls][(i-j+m)mod m] + f[rs][(i-j+m)mod m] + g[u][j]))
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
# define ls(now) (now << 1)
# define rs(now) (now << 1 | 1)
const int M = 10000005 ;
const int N = 5050 ;
const int E = 205 ;
using namespace std ;
int n , m , k ;
int a[M] , b[M] , dep[M] ;
LL f[N][E] , g[N][E] ;
LL sum[N][E] , sumb[N] , sumab[N] ;
unsigned int SA, SB, SC;
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;
}
inline void Clear() {
memset(a , 0 , sizeof(a)) ;
memset(b , 0 , sizeof(b)) ;
memset(f , 31 , sizeof(f)) ;
memset(g , 31 , sizeof(g)) ;
memset(sum , 0 , sizeof(sum)) ;
memset(sumb , 0 , sizeof(sumb)) ;
memset(sumab , 0 , sizeof(sumab)) ;
}
void Read(){
int p, A, B;
scanf("%d%d%d%d%u%u%u%d%d", &n, &k, &m, &p, &SA, &SB, &SC, &A, &B);
for(int i = 1; i <= p; i++)scanf("%d%d", &a[i], &b[i]);
for(int i = p + 1; i <= n; i++){
a[i] = rng61() % A + 1;
b[i] = rng61() % B + 1;
}
}
inline int par(int u) {
while(dep[u] > k + 1)
u /= (1 << (k + 1)) ;
return u ;
}
int main() {
int Case ; scanf("%d",&Case) ;
while(Case --) {
Clear() ;
Read() ;
for(int i = 1 ; i <= n ; i ++) a[i] %= m ;
for(int i = 1 ; i <= n ; i ++) {
dep[i] = dep[i >> 1] + 1 ;
if(a[i] > 0)
sum[par(i)][a[i] - 1] += 1LL * m * b[i] ;
sumb[par(i)] += b[i] ;
sumab[par(i)] += 1LL * a[i] * b[i] ;
}
for(int i = 1 ; i < (1 << (k + 1)) ; i ++)
for(int j = m - 1 ; j >= 0 ; j --) {
sum[i][j] = sum[i][j + 1] + sum[i][j] ;
g[i][j] = j * sumb[i] - sumab[i] + sum[i][j] ;
}
for(int u = (1 << k) ; u < (1 << (k + 1)) ; u ++)
for(int i = 0 ; i < m ; i ++)
f[u][i] = g[u][i] ;
for(int u = (1 << k) - 1 ; u >= 1 ; u --)
for(int i = 0 ; i < m ; i ++)
for(int j = 0 ; j < m ; j ++)
f[u][i] = min( f[u][i] , f[ls(u)][(i - j + m) % m] + f[rs(u)][(i - j + m) % m] + g[u][j] ) ;
printf("%lld
",f[1][0]) ;
}
return 0 ;
}