洛谷p1769 DP(线段树思想)

这题乍一看很吓人,实则当你去手画一下比赛过程的时候就可以知道了,假设总人数有8人,1, 2 , 3 , 4 , 5 , 6 ,7 ,8.
题目说了每一轮都是按编号顺序从小到大去比赛的那么第一轮就是(1,2),(3,4),(5,6),(7,8).这么个比赛顺序,第二轮就是(1,2)的胜利者去比(3,4)的胜利者(另外两个同理)。这个比赛的过程在我刚刚接触这个题的时候我想的是线段树的过程,但是是一个十分完美的区间划分,线段树可能会有(1,3)区间去分开变成(1,2)和(3)的情况,但是这里不会。利用线段树的这个过程我们就可以十分完美的去模拟出这个比赛的过程。接下来考虑我们要怎么得到答案,我先说我一开始想的错误的想法,我最初为了避免小数运算,选择了把胜率当做权值去做,90%概率我就拿90分,然后去计算每个人获胜的总权值去比较大小,但是算权值的时候我用的也是加法,没去想乘法,所以一顿瞎鸡儿操作30分。。。。。
(暴躁改几次还是没用选择打开了题解233333)正确的应该是这样子的我们和线段树一样f[i],i这个节点就代表了一个区间范围[L,R],然后我们在开一维度j表示这个区间内的胜者。f[i][j]的值就代表了j这个人在这个区间获胜的概率。要怎么算呢?首先j这个人是在左边区间来的还是右边区间来的我们可以知道,我们按他在左边儿子(区间)来的,(在右边的同理)。那么这个j节点就是在[L , mid]的区间的胜者,我们用他这个节点是这个区间的胜者的概率f[i * 2][j]去与右区间的每一个胜者获胜的概率去乘上j打赢他们的概率
f[i][j] = sum(f[i*2][j] * f[i * 2 + 1][k]),k就代表了右边区间的所有可能获胜的人。这个式子其实就是一个人在这个区间获胜是概率是在原本所在区间获胜的概率在去分别乘上要赢另一个区间那个获胜的人的概率 在乘上另一个区间那个获胜的人要在她所在原本区间要获胜的概率,有点小饶,在多看两遍。
这样就做完收工了,最后统计[1,R]区间每个人的胜率去得答案就好了

#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<time.h>
#include<string>
#include<cmath>
#include <ctime>
#include<bitset>
#include <cctype>
#define debug cout<<"*********degug**********";
#define int long long
#define RE register
#define yn yn_
using namespace std;
const long long max_ = 1500 + 7;
const int mod = 1e9 + 7;
const int inf = 1e9;
const long long INF = 1e18;
int read() {
	int s = 0, f = 1;
	char ch = getchar();
	while (ch<'0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0'&&ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
inline int min(int a, int b) {
	return a < b ? a : b;
}
inline int max(int a, int b) {
	return a > b ? a : b;
}
int quick_pow(int x, int n) {
	int res = 1;
	while (n > 0) {
		if (n & 1)	res = res * x;
		x = x * x ;
		n >>= 1;//相当于n=n/2.详情请参考位移运算符。
	}
	return res;
}
int n, stander; double f[max_ * 4][max_], nodee[max_][max_];
void update(int node, int L, int R) {
	if (L == R) {
		f[node][L] = 1.0;
		return;
	}
	int mid = (L + R) / 2,L_tree = node * 2 , R_tree = node * 2 + 1;
	update(L_tree, L, mid); update(R_tree, mid + 1, R);
	for (int i = L; i <= mid; i++) {
		double t = 0.0;
		for (int j = mid + 1; j <= R; j++) {
			t =t +  f[R_tree][j] * nodee[i][j] * f[L_tree][i];
		}
		f[node][i] = t ;
	//	printf("[%d,%d]的%d取得胜利的分数:%d\n", L, R, i, f[node][i]);
	}
	for (int i = mid + 1; i <= R; i++) {
		double t = 0.0;
		for (int j = L; j <= mid; j++) {
			t += f[L_tree][j] * nodee[i][j] * f[R_tree][i];
		}
		f[node][i] = t ;
		//printf("[%d,%d]的%d取得胜利的分数:%d\n", L, R, i, f[node][i]);
	}
}

signed main() {
	n = read();
	stander = quick_pow(2, n);
	for (int i = 1; i <= stander; i++) {
		for (int j = 1; j <= stander; j++) {
			int t = read();
			nodee[i][j] = t / 100.0;
			//if (i != j && nodee[i][j] == 0)nodee[i][j] = -inf;
		}
	}
	update(1, 1, stander);
	double ansvalue = 0; int ans = 122345;
	for (int i = 1; i <= stander; i++) {
		if (f[1][i] > ansvalue) {
			ansvalue = f[1][i];
			ans = i;
		}
		else {
			if (f[1][i] == ansvalue && ans > i)ans = i;
		}
	}cout << ans;
	return 0;
}
### 解析洛谷 P3842 线段树题目 #### 题目背景与描述 洛谷平台上的编号为P3842的题目涉及线段树的应用。这类问题通常围绕着一系列的操作,这些操作可能包括查询区间的最值、求和或是更新某些特定位置的数据等。对于具体编号为P3842的问题而言,其核心在于通过构建并维护一棵线段树来高效解决给定的任务。 #### 数据结构的选择——线段树 为了应对频繁发生的区间修改以及询问需求,在此选择了线段树作为主要数据结构[^1]。线段树能够支持快速地对数组中的某个范围执行批量更改,并在此基础上保持高效的查询性能。这使得即使面对较大的输入规模也能保证算法的时间效率处于可接受范围内。 #### 动态规划与时间复杂度分析 当涉及到动态规划(DP)时,如果单纯依靠暴力方法逐一遍历所有可能性,则会面临极高的计算成本。然而借助于线段树优化后的DP方案可以显著降低这一开销至\(O(n\ log\ n)\)[^2]。这意味着在整个过程中不仅实现了功能性的增强同时也兼顾到了运行速度的要求。 #### 实现细节 针对该类问题的具体实现方式如下所示: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 7; int a[MAXN], tree[MAXN << 2]; void pushUp(int rt){ tree[rt] = max(tree[rt<<1],tree[rt<<1|1]); } // 建立线段树 void build(int l,int r,int rt,vector<int>& nums){ if(l==r){ tree[rt]=nums[l]; return ; } int m=(l+r)>>1; build(l,m,rt<<1,nums); build(m+1,r,rt<<1|1,nums); pushUp(rt); } // 更新节点值 (lazy propagation 可选) void updateRange(int L,int R,int C,int l,int r,int rt){ if(L<=l&&r<=R){ // 进行相应更新逻辑 return ; } int m=(l+r)>>1; if(L<=m)updateRange(L,R,C,l,m,rt<<1); if(R>m)updateRange(L,R,C,m+1,r,rt<<1|1); pushUp(rt); } ``` 上述代码片段展示了如何建立一颗基于给定点集`nums`的线段树,并提供了用于更新指定区间内元素的方法框架。实际应用中还需要根据具体的业务场景调整内部逻辑以满足不同类型的请求处理需求。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值