树状数组2

本文介绍了使用树状数组解决两种不同类型的算法问题的具体方法。首先详细解释了如何通过两次扫描序列并应用树状数组来找出特定图腾的个数。接着展示了如何利用树状数组高效地解决谜一样的牛问题,即计算每头牛右侧比它编号大的最近一头牛的位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

241. 楼兰图腾

传送门

241. 楼兰图腾

题目分析

'v’图腾求法

  1. 倒序扫描序列 a a a,利用树状数组求出每个 a [ i ] a[i] a[i]后面有几个数比它大,记录为 r i g h t [ i ] right[i] right[i]
  2. 正序扫描序列 a a a,利用树状数组求出每个 a [ i ] a[i] a[i]前面有几个数比它大,记录为 l e f t [ i ] left[i] left[i]
  3. 依次枚举每一个点为中间点,那么这个点为中心的’v’图腾的个数就是 ∑ i = 1 N l e f t [ i ] × r i g h t [ i ] \sum_{i=1}^{N}left[i]×right[i] i=1Nleft[i]×right[i]

同理’^'图腾求法

  1. 倒序扫描序列 a a a,利用树状数组求出每个 a [ i ] a[i] a[i]后面有几个数比它小,记录为 r i g h t [ i ] right[i] right[i]
  2. 正序扫描序列 a a a,利用树状数组求出每个 a [ i ] a[i] a[i]前面有几个数比它小,记录为 l e f t [ i ] left[i] left[i]
  3. 依次枚举每一个点为中间点,那么这个点为中心的’^’图腾的个数就是. ∑ i = 1 N l e f t [ i ] × r i g h t [ i ] \sum_{i=1}^{N}left[i]×right[i] i=1Nleft[i]×right[i]

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;

inline int yread()
{
	int x = 0, f = 1; char c = getchar();
	while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
	return f * x; 
}

const int SIZE = 200050;
int maxn,n,m,a[SIZE];
ll ans,c[SIZE],rt[SIZE],lf[SIZE];

inline void add(int x, int val)
{
	for (; x <= n; x += x & -x) c[x] += val;
}

inline ll yask(int x)
{
	ll ans = 0;
	for (; x; x -= x & -x) ans += c[x];
	return ans;
}

inline void f1()
{
	for (int i = n; i >= 1; --i)
	{
		rt[i] =yask(maxn) - yask(a[i]);
		add(a[i], 1);
	}
	memset(c,0,sizeof(c));
	for (int i = 1; i <= n; ++i)
	{
		lf[i] =yask(maxn) - yask(a[i]);
		add(a[i], 1);
	}
	for(ll i = 1; i <= n; i++)
    {
        ans += lf[i] * rt[i];
    }
}

inline void f2()
{
	memset(c,0,sizeof(c));
	for (int i = n; i >= 1; --i)
	{
		rt[i] = yask(a[i] - 1);
		add(a[i], 1);
	}
	memset(c,0,sizeof(c));
	for (int i = 1; i <= n; ++i)
	{
		lf[i] = yask(a[i] - 1);
		add(a[i], 1);
	}
	ans = 0;
	for(ll i = 1; i <= n; i++)
    {
        ans += lf[i] * rt[i];
    }
}

int main()
{
	n = yread();
	for (int i = 1; i <= n; ++i)
	{
		a[i] = yread();
		maxn = max(maxn, a[i]);
	}
	f1(); printf("%lld ", ans);
	f2(); printf("%lld\n", ans);
	return 0;
}

244. 谜一样的牛

传送门

244. 谜一样的牛

算法分析

在这里插入图片描述——摘自AcWing 244. 谜一样的牛 by秦淮岸灯火阑珊

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#define ll long long
using namespace std;

inline int yread()
{
	int x = 0, f = 1; char c = getchar();
	while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
	return f * x; 
}

const int SIZE = 100050;
int n,a[SIZE],c[SIZE],ans[SIZE];

inline void add(int x, int val)
{
	for (; x <= n; x += x & -x) c[x] += val;
}

inline ll yask(int x)
{
	ll ans = 0;
	for (; x; x -= x & -x) ans += c[x];
	return ans;
}

int main()
{
	n = yread(); add(1, 1);
	for (int i = 2; i <= n; ++i)
	{
		a[i] = yread();
		add(i, 1);
	}
	for (int i = n; i >= 1; --i)
	{
		int l = 1, r = n;
		while (l < r)
		{
			int mid = l + r >> 1;
			if (yask(mid) < a[i] + 1)
				l = mid + 1;
			else r = mid;
		}
		ans[i] = r;
		add(r, -1);
	}
	for (int i = 1; i <= n; ++i)
		printf("%d\n",ans[i]);
	return 0;
}

总结与反思

  1. 注意初始化
  2. 要掌握灵活运用数据结构的方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值