BZOJ5333 [Sdoi2018]荣誉称号 【差分 + 树形dp】
题目链接
题解
看到式子,立即想到二叉树上一个点及其\(k\)个父亲权值和【如果有的话】模\(m\)意义下为\(0\)
考虑如何满足条件
我们假设\(1\)号为第\(0\)层
那么我们先满足第\(k\)层的条件
由于第\(k + 1\)层也满足条件
由同余的性质第\(k + 1\)层的权值等于第\(1\)层的权值
同理可以往下推
所以在超过第\(k\)层后,每个节点往上都会与某个节点相联结
我们就不妨求出\(w[i][j]\)表示前\(k\)层的节点\(i\)权值变为\(j\)所需要付出的最小代价
显然\(w[i][j]\)就是与\(i\)联结的包括\(i\)的所有节点权值加到\(j\)的代价和
暴力转移是\(O(nm)\)的,但是发现加的是一个等差数列
所以我们双层差分即可\(O(n + 2^{k}m)\)计算出\(w[i][j]\)
然后就可以树形\(dp\)了
有了\(w[i][j]\),我们只需求出第\(k\)层往上权值和为\(0\)的最小代价,其它点就默认满足要求了
我们设\(f[i][j]\)表示点\(i\)到第\(k\)层权值和为\(j\)的最小代价即可
交上去第一个点\(WA\)了,为什么?
考虑\(f[i][j]\)表示的是到第\(k\)层权值和为\(j\),如果这个第\(k\)层根本不存在,这个限制就没有意义了
换言之,\(i\)取什么值都不影响结果
怎么解决这个问题?
我们人为加满至第\(k\)层,多出来的节点的\(b\)设为\(0\)即可
复杂度\(O(n + 2^{k}m^2)\)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 100005,maxm = 100005,N = 10000005;
const LL INF = 100000000000000000ll;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
LL b[N];
int a[N],fa[N],n,m,K;
unsigned int SA, SB, SC;int p, AA, BB;
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(){
    scanf("%d%d%d%d%u%u%u%d%d", &n, &K, &m, &p, &SA, &SB, &SC, &AA, &BB);
    for(int i = 1; i <= p; i++)scanf("%d%lld", &a[i], &b[i]);
    for(int i = p + 1; i <= n; i++){
        a[i] = rng61() % AA + 1;
        b[i] = rng61() % BB + 1;
    }
	if (n < (1 << K + 1) - 1){
		for (int i = n + 1; i < (1 << K + 1); i++)
			a[i] = b[i] = 0;
		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 / (1 << K + 1)];
	}
}
LL w[2100][205];
void calw(){
	int u; cls(w);
	for (int i = 1; i <= n; i++){
		u = fa[i];
		if (!a[i]){
			w[u][1] += b[i];
		}
		else {
			w[u][0] += b[i] * (m - a[i]);
			if (a[i] > 1){
				w[u][1] -= b[i] * (m - a[i] - 1);
				w[u][a[i]] -= b[i] * m;
			}
			else {
				w[u][1] -= 2 * b[i] * (m - 1);
			}
			w[u][a[i] + 1] += b[i] * m;
		}
	}
	int E = min(n,(1 << K + 1) - 1);
	for (int i = 1; i <= E; i++){
		for (int j = 1; j < m; j++)
			w[i][j] += w[i][j - 1];
		for (int j = 1; j < m; j++)
			w[i][j] += w[i][j - 1];
	}
}
LL f[2100][205];
void work(){
	int E = min((1 << K + 1) - 1,n);
	for (int i = E; i; i--){
		int ls = i << 1,rs = i << 1 | 1;
		if (ls > E) ls = 0;
		if (rs > E) rs = 0;
		if (!ls && !rs){
			for (int j = 0; j < m; j++) f[i][j] = w[i][j];
		}
		else if (ls && !rs){
			for (int j = 0; j < m; j++){
				f[i][j] = INF;
				for (int k = 0; k < m; k++){
					f[i][j] = min(f[i][j],w[i][(m + j - k) % m] + f[ls][k]);
				}
			}
		}
		else {
			for (int j = 0; j < m; j++){
				f[i][j] = INF;
				for (int k = 0; k < m; k++){
					f[i][j] = min(f[i][j],w[i][(m + j - k) % m] + f[ls][k] + f[rs][k]);
				}
			}
		}
	}
	printf("%lld\n",f[1][0]);
}
int main(){
	int T = read();
	while (T--){
		gen();
		calw();
		work();
	}
	return 0;
}
												
											BZOJ5333 [Sdoi2018]荣誉称号 【差分 + 树形dp】的更多相关文章
- [loj#2566][BZOJ5333] [Sdoi2018]荣誉称号   树形dp
		
#2566. 「SDOI2018」荣誉称号 休闲游戏玩家小 Q 不仅在算法竞赛方面取得了优异的成绩,还在一款收集钻石的游戏中排名很高. 这款游戏一共有 n 种不同类别的钻石,编号依次为 1 到 n ...
 - bzoj5333: [Sdoi2018]荣誉称号
		
请不要去改题目给的输入,不然你会wa穿... 这么故弄玄虚的题目,肯定要先转换问题 看到这个不断的除2想起别人家的线段树的写法...x的两个孩子是x<<1和x<<1|1 然后问 ...
 - 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
		
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
 - Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]
		
题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_di ...
 - 【BZOJ5333】荣誉称号(动态规划)
		
[BZOJ5333]荣誉称号(动态规划) 题面 BZOJ 洛谷 题解 今天早上贱狗老师讲的.然而我还是不会. 只好照着\(zsy\)代码大力理解一波. 首先观察等式,如果比较熟悉线段树,会发现就是线段 ...
 - [BZOJ4784][ZJOI2017]仙人掌(树形DP)
		
4784: [Zjoi2017]仙人掌 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 312 Solved: 181[Submit][Status] ...
 - 树形dp专题总结
		
树形dp专题总结 大力dp的练习与晋升 原题均可以在网址上找到 技巧总结 1.换根大法 2.状态定义应只考虑考虑影响的关系 3.数据结构与dp的合理结合(T11) 4.抽直径解决求最长链的许多类问题( ...
 - 「树形结构 / 树形DP」总结
		
Codeforces 686 D. Kay and Snowflake 要求$O(n)$求出以每个节点为根的重心. 考虑对于一个根节点$u$,其重心一定在[各个子树的重心到$u$]这条链上.这样就能够 ...
 - Codeforces 348E 树的中心点的性质 / 树形DP / 点分治
		
题意及思路:http://ydc.blog.uoj.ac/blog/12 在求出树的直径的中心后,以它为根,对于除根以外的所有子树,求出子树中的最大深度,以及多个点的最大深度的lca,因为每个点的最长 ...
 
随机推荐
- SOAPUI参数中xml中CDATA包含问题
			
<![CDATA[ <Request> <CardNo>000002629518</CardNo> <SecrityNo/> <BankTr ...
 - JS基础,课堂作业,成绩练习
			
成绩练习 <script> var name = prompt("请输入学生姓名:"); var degree = parseInt(prompt("请输入学 ...
 - 【转】PHPCMS v9 自定义表单添加验证码验证
			
1. 在 \phpcms\templates\default\formguide\show.html 中添加验证码显示 <input type="text" id=&quo ...
 - VS默认的类前缀(访问控制符)是internal
			
VS默认的类前缀(访问控制符)是internal 大家都知道VS默认新建的class的时候,class前面是什么都没有的,按照规则,这个class的可见性是internal,但是说实话,很多人包括我在 ...
 - python sys模块使用详情
			
python常用模块目录 sys模块提供了一系列有关Python运行环境的变量和函数.1.sys.argv可以用sys.argv获取当前正在执行的命令行参数的参数列表(list).变量解释sys.ar ...
 - 1.airflow的安装
			
1.环境准备1.1 安装环境1.2 创建用户2.安装airflow2.1 安装python2.2 安装pip2.3 安装数据库2.4 安装airflow2.4.1 安装主模块2.4.2 安装数据库模块 ...
 - 随机生成30道四则运算-NEW
			
补充:紧跟上一个随机生成30道四则运算的题目,做了一点补充,可以有真分数之间的运算,于是需要在原来的基础上做一些改进. 首先指出上一个程序中的几个不足:1.每次执行的结果都一样,所以不能每天给孩子出3 ...
 - Python:字典操作总结
			
字典是Python中唯一的映射类型 [注]:字典中数据是无序排放的 一.字典的创建方法 方法1:用大括号包裹键值对从而创建字典 addict={}#创建一个空字典 addict={key1:valu ...
 - centos7环境下mysql安装
			
1.去官网下载合适的yum源安装包 https://dev.mysql.com/downloads/repo/yum/ 2.yum 本地安装 命令:yum localinstall mysql57-c ...
 - 福大软工1816 ·软工之404NoteFound团队选题报告
			
目录 NABCD分析引用 N(Need,需求): A(Approach,做法): B(Benefit,好处): C(Competitors,竞争): D(Delivery,交付): 初期 中期 个人贡 ...