[luogu模拟赛] 11.1

//挺好的一套题,就是没控制住看了题解,浪费了
//明天的要好好做

#斐波那契(fibonacci)#
【题目描述】

小 C 养了一些很可爱的兔子。
有一天,小 C 突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行
繁衍: 一对兔子从出生后第二个月起, 每个月刚开始的时候都会产下一对小兔子。 我们假定,
在整个过程中兔子不会出现任何意外。
小 C 把兔子按出生顺序,把兔子们从 1 开始标号,并且小 C 的兔子都是 1 号兔子和 1
号兔子的后代。如果某两对兔子是同时出生的,那么小 C 会将父母标号更小的一对优先标
号。
如果我们把这种关系用图画下来,前六个月大概就是这样的:
这里写图片描述
其中,一个箭头 A → B 表示 A 是 B 的祖先,相同的颜色表示同一个月出生的兔子。
为了更细致地了解兔子们是如何繁衍的,小 C 找来了一些兔子,并且向你提出了 m 个
问题:她想知道关于每两对兔子 ai 和 bi ,他们的最近公共祖先是谁。你能帮帮小 C 吗?
一对兔子的祖先是这对兔子以及他们父母(如果有的话)的祖先,而最近公共祖先是指
两对兔子所共有的祖先中,离他们的距离之和最近的一对兔子。比如,5 和 7 的最近公共祖
先是 2,1 和 2 的最近公共祖先是 1,6 和 6 的最近公共祖先是 6。

【输入格式】

从文件fibonacci.in中读入数据。
第一行,包含一个正整数 n。
输入接下来 n 行,每行包含 2 个正整数,表示 ai 和 bi 。

【输出格式】

输出到文件 fibonacci.out 中。
输入一共 m行,每行一个正整数,依次表示你对问题的答案。

【样例 1 输入】

5
1 1
2 3
5 7
7 13
4 12
【样例 1 输出】

1
1
2
2
4
【样例 2】

见选手目录下的 fibonacci/fibonacci2.in 与 fibonacci/fibonacci2.ans 。

【数据范围与约定】

这里写图片描述

子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解
决一部分数据。
每个测试点的数据规模及特点如下表:

特殊性质 1:保证均为某一个月出生的兔子中标号最大的一对兔子。例如,对
于前六个月,标号最大的兔子分别是 1,2,3,5,8,13
特殊性质 2:保证|ai−aj|≥1。

//第一眼,我擦找LCA???
//后来发现是个找规律题

很容易发现
fa[x] = f[i] - x (f[i] < x <= f[i+1])
但我不会找父亲,很容易就超时了

f(60)=1548008755920,因此
暴力找父亲可能导致超时(O(60)) ;
可以在斐波那契数列上二分( O(log⁡60));
或者直接记录下位置均摊暴力复杂度(查到根总计 60 ,均摊为 O(1) )。

我们把找父亲的复杂度记作O(findfather)。
总复杂度:O(30m×O(findfather))。
最优复杂度:O(60m)。

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

const int maxn = 1000000 + 100;
ll f[maxn],n;

ll read() {
    ll 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 << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * f;
}

void init() {
	f[1] = 1, f[2] = 1;
    for(int i = 3; i <= 60; i++) f[i] = f[i-1] + f[i-2];
}

int main() {
	init();
	n = read();
	for(int i = 1; i <= n; i++) {
		ll a = read(), b = read();
		if(a != b) {
			for(int j = 60; j >= 1; j--) {
				if(a > f[j]) a -= f[j];
				if(b > f[j]) b -= f[j];
				if(a == b) break;
			}
		}
		//注意输出是不要打cout,会卡掉一个点
		printf("%lld\n",a);
	}
    return 0;
}

数颜色(color)
【题目描述】

小 C 的兔子不是雪白的,而是五彩缤纷的。每只兔子都有一种颜色,不同的兔子可能有
相同的颜色。 小 C 把她标号从 1 到 n 的 n 只兔子排成长长的一排, 来给他们喂胡萝卜吃。
排列完成后,第 i 只兔子的颜色是 ci 。
俗话说得好,“萝卜青菜,各有所爱”。小 C 发现,不同颜色的兔子可能有对胡萝卜的
不同偏好。比如,银色的兔子最喜欢吃金色的胡萝卜,金色的兔子更喜欢吃胡萝卜叶子,而
绿色的兔子却喜欢吃酸一点的胡萝卜……为了满足兔子们的要求,小 C 十分苦恼。所以,为
了使得胡萝卜喂得更加准确,小 C 想知道在区间 [lj,rj] 里有多少只颜色为 cj 的兔子。
不过,因为小 C 的兔子们都十分地活跃,它们不是很愿意待在一个固定的位置;与此同
时,小 C 也在根据她知道的信息来给兔子们调整位置。所以,有时编号为 xj 和 xj+1 的两
只兔子会交换位置。
小 C 被这一系列麻烦事给难住了。你能帮帮她吗?

【输入格式】

从文件 color.in 中读入数据。
1 行两个正整数 n,m。
第 2 行 n 个正整数,第 i 个数表示第 i 只兔子的颜色ci。
输入接下来 ? 行,每行为以下两种中的一种:
“1ljrjcj ” :询问在区间 [ l j , r j ] 里 有 多 少 只 颜 色 为 里 有 多 少 只 颜 色 为 c j [l_j ,r_j ] 里有多少只颜色为里有多少只颜色为c_j [lj,rj]cj的兔子;
“2xj ”: xj 和 xi+1 两只兔子交换了位置。

【输出格式】

输出到文件 color.out 中。
对于每个 1 操作,输出一行一个正整数,表示你对于这个询问的答案。

【样例 1 输入】

6 5
1 2 3 2 3 3
1 1 3 2
1 4 6 3
2 3
1 1 3 2
1 4 6 3
【样例 1 输出】

1
2
2
3
【样例 1 说明】

前两个 1 操作和后两个 1 操作对应相同;在第三次的 2 操作后,3 号兔子和 4 号兔子
交换了位置,序列变为 1 2 2 3 3 3。

【样例 2】

见选手目录下的 color/color2.in 与 color/color2.ans 。

【数据范围】

子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解
决一部分测试数据。
对于所有测试点,有 1≤lj < rj≤n,1≤xj < n。
这里写图片描述
特殊性质 1:对于所有操作 1,有 |rj−lj|≤20 或 |rj−lj|≥n?20。:
特殊性质 2:保证不会有两只兔子相同颜色的兔子。

//很坑的T,据说各种数据结构都可以搞搞
//然而简答的只需要二分
//lower_bound,和upper_bound就可以

将兔子按照 (颜色, 位置) 进行双关键字排序。
操作 1 只需要在数组上二分查找;
操作 2 不会改变同种颜色兔子的相对位置,因此只需找到被交换的兔子改掉坐标即可。
时间复杂度 O(N logN),代码复杂度很小(使用 STL 可以短得飞起) 。

(lower_bound 返回一个非递减序列[first, last)中的第一个大于等于值val的位置。
upper_bound返回一个非递减序列[first, last)中第一个大于val的位置。
这里写图片描述

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

const int maxn = 300000 + 100;
int n,m,a[maxn];
vector<int>q[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 << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * f;
}

int main() {
    n = read(), m = read();
    for(int i = 1; i <= n; i++) {
    	a[i] = read();
    	q[a[i]].push_back(i);
	}
	for(int i = 1; i <= n; i++) sort(q[i].begin(),q[i].end());
	for(int i = 1; i <= m; i++) {
		int T = read();
		if(T == 1) {
			int l = read(), r = read(), col = read();
			int p = (upper_bound(q[col].begin(),q[col].end(),r) - lower_bound(q[col].begin(),q[col].end(),l));
			cout<<p<<endl;
		}
		else if(T == 2) {
			int x = read();
			if(a[x] != a[x + 1]) {
				(*lower_bound(q[a[x]].begin() , q[a[x]].end() , x))++;
				(*lower_bound(q[a[x + 1]].begin(), q[a[x + 1]].end(), x + 1))--;
				swap(a[x],a[x + 1]);
			} 
		}
	}
    return 0;
}

分组(division)
【题目描述】

小 C 在了解了她所需要的信息之后,让兔子们调整到了恰当的位置。小 C 准备给兔子
们分成若干个小组来喂恰当的胡萝卜给兔子们吃。
此时, n 只兔子按一定顺序排成一排,第 i 只兔子的颜色是 ai 。由于顺序已经是被
调整好了的,所以每个小组都应当是序列上连续的一段。
在分组前,小 C 发现了一个规律:有些兔子会两两发生矛盾。并且,两只兔子会发生矛
盾,当且仅当代表他们的颜色的数值之和为一个正整数的平方。比如,1 和 2 色兔子
不会发生矛盾, 因为 3 不是任何一个正整数的平方; 而 1 色兔子却会和 3 色兔子发生矛盾,
因为 4=22 。
小 C 认为,只要一个小组内的矛盾不要过大就行。因此,小 C 定义了一个小组的矛盾
值 ? , 表示在这个小组里, 至少需要将这个组再一次分成 k 个小团体; 每个小团体并不需
要是序列上连续的一段,但是需要使得每个小团体内任意两只兔子之间都不会发生矛盾。
小 C 要求,矛盾值最大的小组的矛盾值 k 不超过 K 就可以了。当然,这样的分组方
法可能会有很多个;为了使得分组变得更加和谐,小 C 想知道,在保证分组数量最少的情况
下,什么。你能帮帮她吗?
字典序最小的方案是指,按顺序排列分组的间隔位置,即所有存在兔子 i 和 i + 1 在
不同组的位置 i, 和其它所有相同分组组数相同的可行方案相比总有第一个不同的位置比其
它方案小的方案。

【输入格式】

从文件 division.in 中读入数据。
1 行两个正整数 n,k。
输入第 2 行 n 个正整数,第 i个数表示第 i 只兔子的颜色 ai 。

【输出格式】

输出到文件 division.out 中。
第 1 行一个正整数 m,为你至少需要将兔子分为多少个小组。
输出第 2 行 m?1个从小到大的排列的正整数,第 i 个数 si 表示 si 和 si+1 在
你的方案里被分到了两个小组。如果 m = 1,那么请输出一个空行。

【样例 1 输入】

5 2
1 3 15 10 6
【样例 1 输出】

2
1
【样例 1 解释】

如果将五只兔子全部分到同一个小组的话,那么(1,3)(3,6)(6,10)(10,15)(1,15)均
不能分到同一个小团体;因为最多分成两个小团体,所以为了满足前 4 对限制,只能分为
1,6,15,3,10,但此时不满足 (1,15) ,所以不存在一种组数为 1 的方案满足全部限
制。
如果将五只兔子分为两个小组的话,一种字典序最小的可行的分组方案是 {1}, {3, 15,{1}, {3, 15,
$10, 6},此时第二组内的小团体数量不超过 2 的一种分法是 3,10,15,6。

【样例 2】

见选手目录下的 division/division2.in 与 division/division2.ans 。

【数据范围】

子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解
决一部分数据。
每个测试点的数据规模及特点如下表:
这里写图片描述

特殊性质 1:最优分组方案唯一。
特殊性质 2:保证不会有两只兔子相同颜色的兔子。

//看到这个题我就想是并查集,但不会写QAQ
//有空再改吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值