Daliy Algorithm(线段树&组合数学) -- day 53

Nothing to fear

those times when you get up early and you work hard; those times when you stay up late and you work hard; those times when don’t feel like working — you’re too tired, you don’t want to push yourself — but you do it anyway. That is actually the dream. That’s the dream. It’s not the destination, it’s the journey. And if you guys can understand that, what you’ll see happen is that you won’t accomplish your dreams, your dreams won’t come true, something greater will. mamba out


那些你早出晚归付出的刻苦努力,你不想训练,当你觉的太累了但还是要咬牙坚持的时候,那就是在追逐梦想,不要在意终点有什么,要享受路途的过程,或许你不能成就梦想,但一定会有更伟大的事情随之而来。 mamba out~

2020.4.8


逆序对

使用线段树求解逆序对
思路: 散列 hash
借助树状数组或者线段树帮助序列进行求解逆序对
即查找当前元素前面大的数存在多少
注意:

  1. 数组必须开到数列当中最大元素的大小
  2. 如果数组太大就采用离散化
/*

思路:
1. hash 散列 构造一个hash表 长度要和最大
   注意再进行计算逆序对数量的时候要用 i - query(1 , 1 ,a[i]);


2. 如果出现的数字过大超出了最大申请空间范围
思路 : 离散化

*/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 100005;

struct node{
	int l , r ,cnt;
}tr[N * 4];

int a[N];
int n , mx;
void pushup(int u)
{
	tr[u].cnt = tr[u << 1].cnt + tr[u << 1 | 1].cnt;
}

void build(int u ,int l ,int r)
{
	if(l == r)
	{
		tr[u].l = l, tr[u].r = r;
		tr[u].cnt = 0;
	}else{
		tr[u].l = l, tr[u].r = r;
		int mid = l + r >> 1;
		build(u << 1 , l, mid);
		build(u << 1 | 1, mid + 1 , r);
		pushup(u);
	}
}

int query(int u, int l ,int r)
{
	if(tr[u]. l >= l && tr[u].r <= r)
	{
		return tr[u].cnt;
	}
	int mid = tr[u].l + tr[u].r >> 1;
	int ans = 0;
	if(l <= mid)ans = query(u << 1 , l , r);
	if(r > mid) ans+= query(u << 1 | 1 , l , r);
	return ans; 
}

void modify(int u ,int x , int  v)
{
	if(tr[u].l == tr[u].r)
	{
		tr[u].cnt += v;
	}else{
		int mid = tr[u].l + tr[u].r >> 1;
		if(x <= mid) modify(u << 1 , x , v);
		else modify(u << 1 | 1, x , v);
		pushup(u); 
	}
}

int main()
{
	cin >> n;
	for(int i = 1;i <= n ;i ++){
		scanf("%d",&a[i]);
		mx = max(mx,a[i]);
	}

	build(1 , 1 , mx);
	int ans = 0;
	for(int i = 1;i <= n ;i ++)
	{
		modify(1 , a[i] , 1);
		ans += (i - query(1, 1 , a[i]));
	}
	cout << ans << endl;
	return 0;
}

鸣人的影分身

实际上是组合数学当中的m个苹果放n个盘子问题
一共有如下几种情况:

  1. 就剩一个盘子 或者 没有苹果的时候 只有一种方案
    if(x == 0 || y == 1)return 1;
  2. 如果盘子数 > 苹果数 那么多出来的盘子必定不会放苹果所以舍弃
    if(y > x)
    f(x , x);

3.如果苹果数 > 盘子数:
1. 存在空盘子
即至少有一个盘子为空
f(x , y - 1);
2. 不存在空盘子
即每个盘子最少分一个
f(x - y , y);

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
const int N = 15;
int f[N][N];
int t , m, n;

// 查克拉 苹果
// 影分身 盘子
int dfs(int x , int y)
{
	if(f[x][y] != -1)return f[x][y];
	if(x == 0 || y == 1)return 1; // 当当前苹果数量为零 或者 当前盘子数量为 1 只有一种方案
	if(y > x)return dfs(x , x);   // 如果当前盘子的个数大于苹果的个数 那么剩下的盘子也没用我们最多只用 x 个盘子
	return f[x][y] = dfs(x-y, y) + dfs(x , y-1);
}
int main()
{
	cin >> t;
	while(t--)
	{
		memset(f, -1 , sizeof f);
		cin >> m >> n;
		cout << dfs(m , n) << endl;
	}
	return 0;
}
posted @ 2020-04-08 20:36  _starsky  阅读(149)  评论(0编辑  收藏  举报