2019.03.07【SDOI2018】【洛谷P4620】【BZOJ5333】荣誉称号(DP)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88359378

洛谷传送门

BZOJ传送门


解析:

我TM也不知道这道题的随机有什么性质可以利用,反正我没看出来。

但是有一点是可以确定的,那么小的 k , m k,m 和那么大的 n n 以及完全二叉树的形态不是摆设。

显然一个点最终的权值必须和他的 K + 1 K+1 级祖先相同。

所以我们只需要在前 K K 层维护,将下面的所有点的信息合并上来就行了。

剩下的随便转移出每个点及其子树内部决策完后当前链权值和 % M \%M 为不同值的情况就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
#define u32 unsigned int 

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=2e7+7,K=2050;
int a[N],b[N]; 
int n,k,m;
ll totsum[K],sum[K][203],cost[K][203];
ll f[K][203];
int pre[N];

inline void solve(){
	int tot=1;
	while(tot<=n)tot<<=1;
	n=tot;
	for(int re i=1;i<=n;++i){
		pre[i]=(i>>(k+1))?pre[i>>(k+1)]:i;
		totsum[pre[i]]+=b[i];
		sum[pre[i]][a[i]]+=b[i];
		cost[pre[i]][0]+=(m-a[i])%m*b[i];
	}
	for(int re i=1;i<(1<<(k+1));++i)
	for(int re j=1;j<m;++j){
		cost[i][j]=cost[i][j-1]+totsum[i]-m*sum[i][j];
	}
	for(int re i=(1<<(k-1));i<(1<<k);++i){
		for(int re j=0;j<m;++j){
		f[i][j]=cost[i<<1][(m-j)%m]+cost[i<<1|1][(m-j)%m];
		}
	}
	for(int re i=(1<<(k-1))-1;i;--i){
		for(int re j=0;j<m;++j){
			ll t1=1e18,t2=1e18;
			for(int re k=0;k<m;++k)
			t1=min(t1,f[i<<1][(j+k)%m]+cost[i<<1][k]);
			for(int re k=0;k<m;++k)
			t2=min(t2,f[i<<1|1][(j+k)%m]+cost[i<<1|1][k]);
			f[i][j]=t1+t2;
		}
	}
	ll ans=1e18;
	for(int re i=0;i<m;++i)ans=min(ans,f[1][i]+cost[1][i]);
	cout<<ans<<"\n";
}

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

inline void gen(){
    n=getint(),k=getint(),m=getint(),p=getint(),SA=getint(),SB=getint(),SC=getint(),A=getint(),B=getint();
	for(int re i=1;i<=p;++i)a[i]=getint()%m,b[i]=getint();
    for(int re i=p+1;i<=n;i++){
        a[i]=rng61()%A+1;
        b[i]=rng61()%B+1;
        a[i]%=m;
    }
}

inline void init(){
	memset(a+1,0,sizeof(int)*n);
	memset(b+1,0,sizeof(int)*n);
	gen();
	memset(totsum,0,sizeof totsum);
	memset(sum,0,sizeof sum);
	memset(cost,0,sizeof cost);
}

int T;
signed main(){
	scanf("%d",&T);
	while(T--){
		init();
		solve();
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88359378