qbxt国庆水题记day4

#qbxt国庆水题记#
#day4#


//0 + 20 + 0 == 20
//DP(不会) + 暴力 + 找规律(没找到)


##beautiful##

1.1 题目
从前有一个序列 a[],对于每个 a[i] 都有一个在序列中的优美值,其定义是:序列中最长的
一段 [l,r], 满足 l ≤ i ≤ r,且 a[i] 是这一段的中位数(以数值为第一关键字,下标为第二关键
字排序,这样的话这一段的长度只有可能是奇数),r-l+1 就是它的优美值。
有 Q 个询问,每次给出一段区间,求区间优美值的最大值

1.2 输入
第一行输入 n 接下来 n 个整数,代表 ai 接下来 Q,代表有 Q 个区间接下来 Q 行,每行
两个整数 l, r(l <= r),表示区间的左右端点

1.3 输出
对于每个区间的询问,输出答案

1.4 Sample Input
8
16 19 7 8 9 11 20 16
8
3 8
1 4
2 3
1 1
5 5
1 2
2 8
7 8

1.5 Sample Output
7
3
1
3
5
3
7
3

1.6 数据范围৺㓖定
对于 30% 的数据满足:1 ≤ n, Q ≤ 50
对于 70% 的数据满足:1 ≤ n, Q ≤ 2000
对于 100% 的数据满足,1 ≤ n ≤ 2000, 1 ≤ Q ≤ 100000, 1 ≤ ai ≤ 200

先预处理
以每个i为中心比a[i]大的为1,小的为0
记录左右比a[i]大的为x( 1 - i<= x <= i - 1)的树最靠边的在那
从中间枚举看左右位置和是否为0

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 2000 + 10;
int n,a[maxn];
int l[maxn * 2],r[maxn * 2],cnt,w[maxn],f[maxn][maxn];

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 * 10 + ch - '0';	
		ch = getchar();
	}
	return x * f;
}


int init() {
	for(int i = 1; i <= n; i++) {
		memset(l,255,sizeof(l)); l[n] = 0;
		memset(r,255,sizeof(r)); r[n] = 0;
		cnt = 0;			
		for(int j = i - 1; j > 0; j--) {
			if(a[i] < a[j]) cnt++;
			if(a[i] >= a[j]) cnt--;
			l[n + cnt] = i - j;
		}
		cnt = 0;
		for(int j = i + 1; j <= n; j++) {
			if(a[i] <= a[j]) cnt++;
			if(a[i] > a[j]) cnt--;
			r[n + cnt] = j - i;
		}
		
		for(int j = 1 - i; j <= i - 1; j++) 
		if(l[n + j] >= 0 && r[n - j] >= 0){
			w[i] = max(w[i],l[n + j] + r[n - j] + 1);
		}
	}
}

int main() {
	freopen("beautiful.in","r",stdin);
	freopen("beautiful.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; i++) {
		a[i] = read();
	}
	init();
	
	for(int i = 1; i <= n; i++) {
		f[i][i] = w[i];
		for(int j = i + 1;j <= n; j++) {
			f[i][j] = max(f[i][j-1],w[j]);
		}
	}
	int T = read();
	while(T--) {
		int l = read(), r = read();
		cout<<f[l][r]<<endl;
	}
	return 0;
}

##xor##

2.1 题目᧿䘠
从前有一个长度为 n 的数组 a[],求所有的 lowbit(AixorAj) 之和,其中 1 ≤ i ≤ n 且
1 ≤ j ≤ n
输出答案对 998244353 取模
科普:
xor 指异或操作,在 C++ 中的运算符号为ˆ,运算法则为:0 xor 0 = 0, 0 xor 1 = 1, 1 xor
0 = 1, 1 xor 1 = 0
lowbit(x) = 2k,其中 k 为最小的满足 2k and x ̸= 0 的非负整数

2.2 输入Ṭᔿ
第一行一个整数 T,表示数据组数
对于每组数据
第一行一个正整数 n,表示数组长度
第二行 n 个非负整数,第 i 个整数为 Ai

2.3 输出Ṭᔿ
每组数据输出一行 Case #x: ans。x 表示组数编号,从 1 开始。ans 为所求值。

2.4 样ֻ输入输出
2.5 Sample Input
2
5
4 0 2 7 0
5
2 6 5 4 0

2.6 Sample Output
Case #1: 36
Case #2: 40

2.7 数据范围৺㓖定
所有的 Ai 都在 int 范围内,所有数据满足 1 ≤ T ≤ 10

测试点 n, m
1, 2, 3 ≤ 1000
4, 5, 6 ≤ 5000
7, 8, 9, 10 ≤ 5 ∗104

//写过树状数组竟然不知道lowbit(lowbit可以60)~~~~~~
lowbit

int lowbit(int x){
	return x&(-x);
}

正解是分治+tire树的些思想
相当于建立了一个由01构成的trie树
对于每一个节点的子节点左边表示为1 右边表示为0
数目相当于左子树 * 右子树
类似有归并排序(当然trie也可以暴力做

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int mod = 998244353;
const int maxn = 50000 + 100;
int n,T;
long long sum;
int a[maxn];

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 * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}

void dfs(int l ,int r, int k) {
	if(l >= r || k > 30) return;
	int p = l - 1;
	for(int i = l; i <= r; i++) {
		if(a[i] & (1<<k)) {
			p++;
			swap(a[i],a[p]);
		}
	}
	sum += (long long)(r - p) * (p - l + 1) % mod * (1<<k) % mod;
	sum %= mod;
	dfs(l,p,k + 1);
	dfs(p+1,r,k + 1);
}

void work(int t) {
	n = read();
	for(int i = 1; i <= n; i++) {
		a[i] = read();
	}
	sum = 0;
	dfs(1,n,0);
	sum = (sum * 2) % mod;
	printf("Case #%d: %d\n",t,sum);
}

int main() {
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
	T = read();
	for(int i = 1; i <= T; i++) {
		work(i);
	}
	return 0;
}

##Permutation##

3.1 题目᧿䘠
从前,有一个长度为 n 的排列 p
每次把排列的每个循环拿出来,写成标准循环,再做一次排序
循环:排列第 i 位为 p[i],如果我们令 i 点到 p[i] 点连一条边,那么会形成若干个简单环,
每个简单环所有的 p[i] 就构成一个循环
比如排列 [4, 1, 6, 2, 5, 3],有 3 个循环 (421)(63)(5)
每个循环从任意一个位置开始读都是一样的
比如 (412) 也是 (124),(241)。n 个循环就一共 n 个表达法
我们规定一个标准循环是以循环内最大的数字开头
循环之间排序的关键字就是第一个数字的大小
如 (421)(63)(5) 排序后是 (421)(5)(63)
如果排序后的排列和原排列一样,那么就是可行排列
求 n 个数的字典序第 k 大的可行排列

3.2 输入Ṭᔿ
两个整数,n,k
保证 k 在 long long 范围内,保证有解

3.3 输出Ṭᔿ
n 个整数,表示满足条件的排列

3.4 样ֻ输入 1
4 3

3.5 样ֻ输出 1
1 3 2 4

3.6 样ֻ解䟺 1
n=4 时,字典序最小的三个可行排列依次是
1 2 3 4
1 2 4 3
1 3 2 4

3.7 样ֻ输入 2
10 1
3.8 样ֻ输出 3
1 2 3 4 5 6 7 8 9 10

3.9 数据范围的约定
对于 30% 的数据满足:1 ≤ n ≤ 10
对于 100% 的数据满足,1 ≤ n ≤ 50

奇葩的结论(竟然与裴波那契数列有关)
可以发现只有两个紧挨着的数发生交换才为可行排列
以5为例
1 2 3 4 5 – 1
1 2 3 5 4 – 1 + 1
1 2 4 3 5 –
1 3 2 4 5 – 3 + 1
1 3 2 5 4 –
2 1 3 4 5 – 5 + 1
2 1 3 5 4 –
2 1 4 3 5 –
所以每次交换后有为第f[i]位(f[i]为裴波那契数列)
递推下即可

代码

#include<iostream>
#include<cstdio>
using namespace std;

const int maxn = 100;
int a[maxn],n;
long long k,f[maxn];

void dfs(int val,long long k) {
	if(!k) return;
	for(int i = val; i < n;i++) {
		if(k > f[n - i]) {
			swap(a[i],a[i + 1]);
			dfs(val + 2, k - f[n - i]);
			return; 
		}
	}
}

int main() {
	freopen("Permutation.in", "r", stdin);
    freopen("Permutation.out", "w", stdout);
	cin>>n>>k;
	f[0] = 1, f[1] = 1;
	for(int i = 2; i <= n; i++) f[i] = f[i-1] + f[i-2];
	for(int i = 1; i <= n; i++) a[i] = i;
	dfs(1,k);
	for(int i = 1; i <= n; i++) cout<<a[i]<<' ';
	cout<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值