• 【BZOJ5333】荣誉称号(动态规划)


    【BZOJ5333】荣誉称号(动态规划)

    题面

    BZOJ
    洛谷

    题解

    今天早上贱狗老师讲的。然而我还是不会。
    只好照着(zsy)代码大力理解一波。
    首先观察等式,如果比较熟悉线段树,会发现就是线段树的前(k)个祖先
    而线段树是完全二叉树,也就所有东西形成了一个完全二叉树。
    并且任意节点和它的前(k)次祖先的和都要是(0)(以下都是在模(m)意义下)
    所以,我们可以轻易推出一个结论,(x)节点和(x)(k)次祖先同余。
    所以,我们只需要考虑前(k)层就好了,剩下的点全部可以按照同余的关系归并到了一起。
    这样子节点个数就从(10^7)降到了(2^{11})
    现在也就是任意一个叶子节点到根节点的和都是要(0)
    那么直接(dp)
    (f[i][j])表示第(i)个节点到达它所有儿子的路径和都是(j)的最小代价。
    转移的时候考虑一下儿子的权值是多少以及当前点是多少。
    当前点变成某个权值的代价可以提前预处理。
    这样子复杂度就是(O(2^km^2))

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define MAX 11111111
    #define W 2050
    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,k,m,a[MAX],b[MAX],fa[MAX];
    ll val[W][200],sum[W],cal[W][200],f[W][200];
    void init()
    {
    	memset(val,0,sizeof(val));memset(sum,0,sizeof(sum));
    	memset(cal,0,sizeof(cal));memset(f,63,sizeof(f));
    	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;
    	for(int i=n+1;i<(1<<(k+1));++i)a[i]=b[i]=0;n=max(n,(1<<(k+1))-1);
    	for(int i=1;i<=n;++i)
    	{
    		a[i]%=m;
    		if(i<(1<<(k+1)))fa[i]=i;
    		else fa[i]=fa[i>>(k+1)];
    		val[fa[i]][0]+=a[i]?b[i]*(m-a[i]):0;
    		sum[fa[i]]+=b[i];cal[fa[i]][a[i]]+=b[i]*m;
    	}
    	for(int i=1;i<(1<<(k+1));++i)
    		for(int j=1;j<m;++j)
    			val[i][j]=val[i][j-1]+sum[i]-cal[i][j];
    	
    }
    int main()
    {
    	int T;scanf("%d",&T);
    	while(T--)
    	{
    		init();
    		for(int i=1<<k;i<(1<<(k+1));++i)
    			for(int j=0;j<m;++j)f[i][j]=val[i][j];
    		for(int i=(1<<k)-1;i;--i)
    			for(int j=0;j<m;++j)
    				for(int l=0;l<m;++l)
    					f[i][j]=min(f[i][j],f[i<<1][(j-l+m)%m]+f[i<<1|1][(j-l+m)%m]+val[i][l]);
    		printf("%lld
    ",f[1][0]);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    User-Agent大全
    Python yield 使用浅析
    解决Ubuntu终端里面显示路径名称太长
    百度搜索URL中的参数都是什么
    Fiddler 网页采集抓包利器__手机app抓包
    《samba服务配置的文本》
    《samba服务搭建》RHEL6
    《NFS文件共享服务的搭建》RHEL
    《shell脚本if..then..elif..then.if语句的总结》
    《shell条件测试语句,字符串测试apache是否开启》
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9255553.html
Copyright © 2020-2023  润新知