首页 > 其他分享 >动态规划树状数组优化(M元上升子序列)

动态规划树状数组优化(M元上升子序列)

时间:2022-10-29 20:24:22浏览次数:95  
标签:ch 树状 int res LL 数组 序列 sum define

我们先来看简单版:P1637 三元上升子序列

这道题显然考虑dp,转移式子也很好写

设f[i][j]表示以a[j]结尾长度为i的上升子序列个数。显然答案就是 \(\sum\limits_{k=1}^{n} f_{3,k}\)

\[f_{i,j}=\sum\limits_{k=i}^{j-1}[a_k<a_j]f_{i-1,k} \]

代码:

#include <bits/stdc++.h>
#define rei register int
#define LL long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define cvar int n, m, T;
#define rep(i, s, n, c) for (register int i = s; i <= n; i+=c)
#define repd(i, s, n, c) for (register int i = s; i >= n; i-=c)
#define CHECK cout<<"WALKED"<<endl;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
#define pb push_back
#define ls id<<1
#define rs id<<1|1
const int INF = INT_MAX;
long long binpow(long long a, long long b, LL mod){long long res = 1;  while (b > 0){if (b & 1) res = res * a % mod;a = a * a % mod;  b >>= 1;  }  return res;}
using namespace std;

const int maxn = 30010;
int n;
LL a[maxn];
LL f[4][maxn]; // 长度i,a[j]为结尾的个数

int main()
{
	n = read();
	rep(i, 1, n, 1) {
		a[i] = read();
		f[1][i] = 1;
	}
	rep (i, 1, 3, 1) {
		rep (j, 1, n, 1) {
			rep(k, 1, j - 1, 1) {
				if (a[k] < a[j])
					f[i][j] += f[i - 1][k];
			}
		}
	}
	LL ans = 0;
	rep (i, 1, n, 1) ans += f[3][i];
	cout << ans << endl;
	return 0;
}

别急,会T,考虑优化。

发现可以优化的地方是每次的求和。但是单单是一个前缀和恐怕不行,因为还要满足条件 \(a_k<a_j\),所以考虑将数组离散化一下,这样数组 a 中存储的就是排名,然后以 \(a_i\) 为树状数组下标存储,这样就完美的解决了大小问题。

#include <bits/stdc++.h>
#define rei register int
#define LL long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define cvar int n, m, T;
#define rep(i, s, n, c) for (register int i = s; i <= n; i+=c)
#define repd(i, s, n, c) for (register int i = s; i >= n; i-=c)
#define CHECK cout<<"WALKED"<<endl;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
#define pb push_back
#define ls id<<1
#define rs id<<1|1
const int INF = INT_MAX;
long long binpow(long long a, long long b, LL mod){long long res = 1;  while (b > 0){if (b & 1) res = res * a % mod;a = a * a % mod;  b >>= 1;  }  return res;}
using namespace std;

const int maxn = 30010;
int n, m;
//  树状数组
LL c[maxn]; // 2?
inline int lowbit(LL x) {
	return x & -x;
}
inline LL query(int x) {
	LL sum = 0;
	while (x)
	{
		sum += c[x];
		x -= lowbit(x);
	}
	return sum;
}
inline void add(int x, LL val) {
	while (x <= n) { // n ? m ?
		c[x] += val;
		x+= lowbit(x);
	}
}
//


LL a[maxn];
LL f[4][maxn]; // 长度i,a[j]为结尾的个数

// 离散化
LL s[maxn];
void disp()
{
	stable_sort(s + 1, s + n + 1);
	m = unique(s + 1, s + n + 1) - s - 1;
	rep (i, 1, n, 1)
		a[i] = lower_bound(s + 1, s + m + 1, a[i]) - s;
	
}
//

int main()
{
	n = read();
	rep(i, 1, n, 1) {
		a[i] = read();
		s[i] = a[i];
		f[1][i] = 1;
	}
	
	disp();
	
	rep (i, 2, 3, 1) {
		memset(c, 0, sizeof(c));
		rep (j, 1, n, 1) {
			f[i][j] = query(a[j] - 1);
			add(a[j], f[i - 1][j]);
		}
	}
	LL ans = 0;
	rep (i, 1, n, 1) ans += f[3][i];
	cout << ans << endl;
	return 0;
}

这道题就过了。

那么显然,这道题可以扩展到 M 元上升子序列

UVA12983 The Battle of Chibi

#include <bits/stdc++.h>
#define rei register int
#define LL long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define cvar int n, m, T;
#define rep(i, s, n, c) for (register int i = s; i <= n; i+=c)
#define repd(i, s, n, c) for (register int i = s; i >= n; i-=c)
#define CHECK cout<<"WALKED"<<endl;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
#define pb push_back
#define ls id<<1
#define rs id<<1|1
const int INF = INT_MAX;
long long binpow(long long a, long long b, LL mod){long long res = 1;  while (b > 0){if (b & 1) res = res * a % mod;a = a * a % mod;  b >>= 1;  }  return res;}
using namespace std;

const int maxn = 1010;
const LL mod = 1e9 + 7;
int n, m;
//  树状数组
LL c[maxn]; // 2?
inline int lowbit(LL x) {
	return x & -x;
}
inline LL query(int x) {
	LL sum = 0;
	while (x)
	{
		sum = (sum + c[x]) % mod;
		x -= lowbit(x);
	}
	return sum;
}
inline void add(int x, LL val) {
	while (x <= m) { // n ? m ?
		c[x] = (c[x] + val) % mod;
		x+= lowbit(x);
	}
}
//


LL a[maxn];
LL f[maxn][maxn]; // 长度i,a[j]为结尾的个数

// 离散化
LL s[maxn];
void disp()
{
	stable_sort(s + 1, s + n + 1);
	m = unique(s + 1, s + n + 1) - s - 1;
	rep (i, 1, n, 1)
		a[i] = lower_bound(s + 1, s + m + 1, a[i]) - s;
	
}
//

int main()
{
	int T = read(), cnt = 1;
	int kkk;
	while (T--)
	{
		n = read(); kkk = read();
		rep(i, 1, n, 1) {
			a[i] = read();
			s[i] = a[i];
			f[1][i] = 1;
		}
		
		disp();
		
		rep (i, 2, kkk, 1) {
			memset(c, 0, sizeof(c));
			rep (j, 1, n, 1) {
				f[i][j] = query(a[j] - 1);
				add(a[j], f[i - 1][j]);
			}
		}
		LL ans = 0;
		rep (i, 1, n, 1) ans = (ans + f[kkk][i]) % mod;
		printf("Case #%d: %lld\n", cnt, ans);
		cnt++;
	}
	return 0;
}

标签:ch,树状,int,res,LL,数组,序列,sum,define
From: https://www.cnblogs.com/CYLSY/p/16839565.html

相关文章

  • 60-ES10-数组方法扩展-flat与flatMap
     ......
  • Serialzable和Parcelable的区别?Bunder传递对象为什么需要序列化?
    1Bunder传递对象为什么需要序列化?因为bundle传递数据时只支持基本数据类型,所以在传递对象时需要序列化转换成可存储或可传输的本质状态(字节流)。序列化后的对象可以在网络、......
  • 数组常用方法
    一、数组常用方法1.unshift(),从首位添加数据至原数组中,返回新数组的长度2.push(),从末位添加数据至原数组中,返回新数组的长度3.shift(),去掉数组的第一个......
  • 7种数组去重方法
    一、设定原数组 constarr=[22,22,'ture','ture',false,false,undefined,undefined,null,null,NaN,NaN,'NaN',0,0,'a','a',15,15,true,true,{},......
  • P5826 【模板】子序列自动机
    题目链接P5826【模板】子序列自动机【模板】子序列自动机题目背景本题中,若\(x\)是\(y\)的子序列,则等价于存在一个单调递增序列\(z\),满足\(|z|=|x|\),\(z_{|x|}......
  • Shell脚本之数组
    概念数组(Array)是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。......
  • js 字符串中包含逗号和分号分析成数组
    varstr="117.39755436808615,34.59211450864094;117.39783481906638,34.59185738594207;117.39825396841732,34.59151467824745;117.39895365857903,34.5909999082......
  • Shell脚本之数组排序
    数组排序(使用tr、sort、for)操作步骤;使用tr命令将数组内每个元素之间的空格替换为换行符;之后使用sort命令按从小到大重新排序;最后使用for循环遍历排序后的元素值。......
  • #yyds干货盘点# 动态规划专题:最长公共子序列(一)
    1.简述:描述给定两个字符串s1和s2,长度为n和m 。求两个字符串最长公共子序列的长度。所谓子序列,指一个字符串删掉部分字符(也可以不删)形成的字符串。例如:字符串"arcae......
  • js 数组-过滤数据
    js数组-过滤数据filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。注意:filter()不会对空数组进行检测。注意:filter()不会改......