[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 ” :詢問在區間 [lj,rj]cj[l_j ,r_j ] 裏有多少隻顏色爲裏有多少隻顏色爲c_j的兔子;
“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
//有空再改吧

發佈了68 篇原創文章 · 獲贊 25 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章