妈妈我能独立切「SDOI2018R2」的题啦!
虽然这个题不难
向下取整满足结合律(并不知道为啥,asuldb说这很显然)。然后把那个鬼畜的条件拆开(懒得打向下取整了):
$a_{x/2}+a_{x/2/2}+a_{x/2/2/2}+…\equiv 0 \pmod m$
如果连上边数列就会变成这个样子:
很明显构成了一棵完全二叉树。这个条件就能转化为从任意点出发,向下延伸包含$k+1$个点的路径权值和模$m$等于$0$。
再看上面那张图,令$k=2$,列两个式子:
$a_1+a_2+a_4\equiv 0\pmod m$
$a_2+a_4+a_8\equiv 0\pmod m$
$\therefore a_1\equiv a_8\pmod m$
同理,$a_1\equiv a_9\equiv a_{10}\equiv…a_{15}$,$a_2\equiv a_{16}\equiv a_{17}\equiv … a_{23}\pmod m$
也就是说每个点与其子树内向下第$k+1$层的点最终的权值相等。
这样只要前$k+1$层的路径满足模$m$等于$0$,下面的所有路径就都满足。$DP$的时候要考虑的点数直接降到$2^{k+1}$个。
设$f(i,j)$为点$i$向下延伸至$k+1$层路径权值和模$m$为$j$的最小花费。
$O(n+m^22^{k+1})$预处理一个$v(i,j)$表示将点$i$以及与它相等的点权值全部改为$j$的花费。
直接枚举左右儿子的路径权值转移:
$f(i,j)=\min\{f(i<<1,k)+f(i<<1|1,k)+v(i,(j-k+m)\%m)\}$
从$2^{k+1}$开始倒着$DP$,答案就是$f(1,0)$。复杂度$O(Tn+Tm^22^{k+1})$。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 10000005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
int n,m,k,a[maxn],b[maxn],id[maxn];
long long f[1<<11][200],v[1<<11][200],tax[1<<11][200];
namespace file{
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;
}
void gen(){
n=read(),k=read()+1,m=read(),p=read(),SA=read<unsigned>(),SB=read<unsigned>(),SC=read<unsigned>(),A=read(),B=read();
for(int i = 1; i <= p; i++)a[i]=read()%m,b[i]=read();
for(int i = p + 1; i <= n; i++){
a[i] = (rng61() % A + 1)%m;
b[i] = rng61() % B + 1;
}
}
}
int main(){
int t=read();
while(t--){
file::gen();
memset(v,0,sizeof v);
memset(tax,0,sizeof tax);
memset(f,0x3f,sizeof f);
for(register int i=1;i<1<<k;++i)tax[id[i]=i][a[i]]=b[i];
for(register int i=1<<k;i<=n;++i)tax[id[i]=id[i/(1<<k)]][a[i]]+=b[i];
for(register int i=1;i<1<<k;++i){
for(register int j=0;j<m;++j){
for(register int k=0;k<j;++k)
v[i][j]+=tax[i][k]*(j-k);
for(register int k=j+1;k<m;++k)
v[i][j]+=tax[i][k]*(j+m-k);
}
}
for(register int i=1<<k-1;i<1<<k;++i)
for(register int j=0;j<m;++j)
f[i][j]=v[i][j];
for(register int i=(1<<k-1)-1;i;--i)
for(register int j=0;j<m;++j)
for(register int k=0;k<m;++k)
f[i][j]=min(f[i][j],f[i<<1][k]+f[i<<1|1][k]+v[i][(j-k+m)%m]);
printf("%lld\n",f[1][0]);
for(register int i=1;i<=n;++i)a[i]=b[i]=0;
}
}