主頁 >  其他 > 第14屆藍橋杯C++B組省賽題解(A-J)(更新完畢)

第14屆藍橋杯C++B組省賽題解(A-J)(更新完畢)

2023-04-25 08:21:57 其他

目錄
  • A. 日期統計
    • 題目內容
    • 思路
    • 代碼
    • 答案
  • B.01 串的熵
    • 題目內容
    • 思路
    • 代碼
    • 答案
  • C.冶煉金屬
    • 題目內容
    • 輸入格式
    • 輸出格式
    • 輸入樣例
    • 輸出樣例
    • 思路
    • 代碼
  • D.飛機降落
    • 題目內容
    • 輸入格式
    • 輸出格式
    • 輸入樣例
    • 輸出樣例
    • 思路
    • 代碼
  • E.接龍數列
    • 題目內容
    • 輸入格式
    • 輸出格式
    • 輸入樣例
    • 輸出樣例
    • 思路
    • 代碼
  • F.島嶼數量
    • 題目內容
    • 輸入格式
    • 輸出格式
    • 輸入樣例
    • 輸出樣例
    • 思路
    • 代碼
  • G.子串簡寫
    • 題目內容
    • 輸入格式
    • 輸出格式
    • 輸入樣例
    • 輸出樣例
    • 思路
    • 代碼
  • H.整數洗掉
    • 題目內容
    • 輸入格式
    • 輸出格式
    • 輸入樣例
    • 輸出樣例
    • 思路
    • 代碼
  • I.景區導游
    • 題目內容
    • 輸入格式
    • 輸出格式
    • 輸入樣例
    • 輸出樣例
    • 思路
    • 代碼
  • J.砍樹
    • 題目內容
    • 輸入格式
    • 輸出格式
    • 輸入樣例
    • 輸出樣例
    • 思路
    • 代碼

A. 日期統計

題目內容

小藍現在有一個長度為 \(100\) 的陣列,陣列中的每個元素的值都在 \(0\)\(9\) 的范圍之內,
陣列中的元素從左至右如下所示:

5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2 7
0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8 2 7 6 8 9 5 6 5 6 1 4 0 1
0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3

現在他想要從這個陣列中尋找一些滿足以下條件的子序列:

  1. 子序列的長度為 \(8\)
  2. 這個子序列可以按照下標順序組成一個 \(yyyymmdd\) 格式的日期,并且
    要求這個日期是 \(2023\) 年中的某一天的日期,例如 \(20230902,20231223\)
    \(yyyy\) 表示年份,\(mm\) 表示月份,\(dd\) 表示天數,當月份或者天數的長度只有一位時需要一個前導零補充,
    請你幫小藍計算下按上述條件一共能找到多少個不同的 \(2023\) 年的日期,
    對于相同的日期你只需要統計一次即可,
    本題的結果為一個整數,在提交答案時只輸出這個整數,輸出多余的內容將無法得分,

思路

八重回圈列舉日期+set去重即可
\(Tips:\) 前4重特判2023,否則程式會跑的很慢

代碼

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n=100;
int num[N];
set<string>s;

bool check(string all)
{
	int m=stoi(all.substr(0,2));
	int d=stoi(all.substr(2,2));
	
	int mon[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
	if(m>=1&&m<=12)
	{
		if(d>=1&&d<=mon[m])
			return true;
	}
	
	return false;
}
int main()
{
	for(int i=0;i<n;i++)cin>>num[i];



	for(int a=0;a<n;a++)
	{
	
		if(num[a]!=2)continue;
		for(int b=a+1;b<n;b++)
		{
			if(num[b]!=0)continue;
			for(int c=b+1;c<n;c++)
			{
				if(num[c]!=2)continue;
				for(int d=c+1;d<n;d++)
				{	
					if(num[d]!=3)continue;
					for(int e=d+1;e<n;e++)
					{
						for(int f=e+1;f<n;f++)
						{
							for(int g=f+1;g<n;g++)
							{
								for(int h=g+1;h<n;h++)
								{
									string n1=to_string(num[e]);
									string n2=to_string(num[f]);
									string n3=to_string(num[g]);
									string n4=to_string(num[h]);
									string all=n1+n2+n3+n4;
									if(check(all))s.insert(all);
									
									
								}
							}
						}
					}
				}
			}
		}
	}	
	
	
	cout<<s.size()<<endl;
	
	return 0;
}

答案

235

B.01 串的熵

題目內容

對于一個長度 $ n $ 的 \(01\)\(S = x_1x_2x_3...x_n\)
香農資訊熵的定義為:\(H(S)=-\sum_{i=1}^{n}p(x_i)log_2(p(x_i))\) ,其中 \(p(0)\), \(p(1)\) 表示在這個 \(01\) 串中 \(0\)\(1\) 出現的占比,
比如,對于 \(S = 100\) 來說,資訊熵 \(H(S) = - \frac{1}{3}log_2(\frac{1}{3}) - \frac{2}{3} log_2(\frac{2}{3}) = 1.3083\)
對于一個長度為 \(23333333\)\(01\) 串,如果其資訊熵為 \(11625907.5798\) ,且 \(0\) 出現次數比 \(1\) 少,那么這個 \(01\) 串中 \(0\) 出現了多少次?
本題的結果為一個整數,在提交答案時只輸出這個整數,輸出多余的內容將無法得分,

思路

按題意模擬即可,由題意可得 \(H(S)\)的值只與 \(01\) 出現的次數有關,因為 \(0\) 出現次數比 \(1\) 少,所以可以從 \(\lfloor \frac{23333333}{2} \rfloor = 11666666\) 開始往下列舉0的個數,同時計算 \(p(0),p(1)\) 的占比,帶入公式驗證是否相等,注意設定誤差范圍去判斷浮點數是否相等

代碼

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-4;
//#define double long double
int len;

int main()
{
	int len=23333333;
	for(int i=len/2;i>=1;i--)
	{
		double px0=1.0*i/len,px1=1.0*(len-i)/len;
		double H=-(i*px0*log2(px0)+(len-i)*px1*log2(px1));
		if(fabs(H-11625907.5798)<=eps)
		{
			cout<<i<<endl;
			return 0;
		}
	}
	
	return 0;
}

答案

11027421

C.冶煉金屬

題目內容

小藍有一個神奇的爐子用于將普通金屬 \(O\) 冶煉成為一種特殊金屬 \(X\) ,這個爐子有一個稱作轉換率的屬性 \(V\)\(V\) 是一個正整數,這意味著消耗 \(V\) 個普通金屬 \(O\) 恰好可以冶煉出一個特殊金屬 \(X\),當普通金屬 \(O\) 的數目不足 \(V\) 時,無法繼續冶煉,現在給出了 \(N\) 條冶煉記錄,每條記錄中包含兩個整數 \(A\)\(B\),這表示本次投入了 \(A\) 個普通金屬\(O\),最終冶煉出了 \(B\) 個特殊金屬 \(X\),每條記錄都是獨立的,這意味著上一次沒消耗完的普通金屬 \(O\) 不會累加到下一次的冶煉當中,根據這 \(N\) 條冶煉記錄,請你推測出轉換率 \(V\) 的最小值和最大值分別可能是多少,題目保證評測資料不存在無解的情況,

輸入格式

第一行一個整數 \(N\),表示冶煉記錄的數目,
接下來輸入 \(N\) 行,每行兩個整數 \(A、B\) ,含義如題目所述,
對于 \(30\%\) 的評測用例,\(1 ≤ N ≤ 100\)
對于 \(60\%\) 的評測用例,\(1 ≤ N ≤ 1000\)
對于 \(100\%\) 的評測用例,\(1 ≤ N ≤ 10000,1 ≤ B ≤ A ≤ 1,000,000,000\)

輸出格式

輸出兩個整數,分別表示 \(V\) 可能的最小值和最大值,中間用空格分開,

輸入樣例

3
75 3
53 2
59 2

輸出樣例

20 25

思路

求最小值和最大值問題,可以利用二分答案進行判斷,

  • 求最小值

    • 假設最終答案為 \(S\)
      • 因為 \(S\) 的最優性,若要求答案 \(<S\),對于每組金屬 \(A\) 至少有一個不能冶煉出 \(B\) 個特殊金屬
      • 若答案可以 \(>S\),則一定存在一個屬性 \(V\) ,使得每組金屬 \(A\) 都能冶煉出對應的 \(B\) 個特殊金屬,最優解就處于可行性的分界點上
  • 求最大值與上面同理

代碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N],b[N];
int n;

bool check_min(int x)
{
	//如果某一組存在a[i]/x的值比實際B大,說明V可以繼續增加
	for(int i=0;i<n;i++)
		if(a[i]/x>b[i])return false;
	
	return true;
}

bool check_max(int x)
{
	//如果某一組存在a[i]/x的值比實際B小,說明V可以繼續減小
	for(int i=0;i<n;i++)
		if(a[i]/x<b[i])return false;
	
	return true;
}
int main()
{
	cin>>n;
	
	for(int i=0;i<n;i++)cin>>a[i]>>b[i];
	
	
	int l=0,r=1e9;
	
	//求最小值
	while(l<r)
	{
		int mid=l+r>>1;
		if(check_min(mid))r=mid;
		else l=mid+1;
	}
	
	cout<<l<<' ';
	
	
	l=0,r=1e9;
	
	//求最大值
	while(l<r)
	{
		int mid=l+r+1>>1;
		//
		if(check_max(mid))l=mid;
		else r=mid-1;
	}
	
	cout<<l<<endl;
	
	return 0;
}

D.飛機降落

題目內容

\(N\) 架飛機準備降落到某個只有一條跑道的機場,其中第 \(i\) 架飛機在 \(T_i\) 時刻到達機場上空,到達時它的剩余油料還可以繼續盤旋 \(D_i\) 個單位時間,即它最早可以于 \(T_i\) 時刻開始降落,最晚可以于 \(T_i + D_i\) 時刻開始降落,降落程序需要 \(L_i\) 個單位時間,一架飛機降落完畢時,另一架飛機可以立即在同一時刻開始降落,但是不能在前一架飛機完成降落前開始降落,請你判斷 \(N\) 架飛機是否可以全部安全降落,

輸入格式

輸入包含多組資料,
第一行包含一個整數 \(T\) ,代表測驗資料的組數,
對于每組資料,第一行包含一個整數 \(N\)
以下 \(N\) 行,每行包含三個整數:\(T_i,D_i\)\(L_i\)
對于 \(30\%\) 的資料,\(N ≤ 2\)
對于 \(100\%\) 的資料,\(1 ≤ T ≤ 10,1 ≤ N ≤ 10,0 ≤ T_i,D_i,L_i ≤ 100,000\)

輸出格式

對于每組資料,輸出 \(YES\) 或者 \(NO\),代表是否可以全部安全降落,

輸入樣例

2
3
0 100 10
10 10 10
0 2 20
3
0 10 20
10 10 20
20 10 20

輸出樣例

YES
NO

對于第一組資料:
安排第 \(3\) 架飛機于 \(0\) 時刻開始降落,\(20\) 時刻完成降落,
安排第 \(2\) 架飛機于 \(20\) 時刻開始降落,\(30\) 時刻完成降落,
安排第 \(1\) 架飛機于 \(30\) 時刻開始降落,\(40\) 時刻完成降落,
對于第二組資料,無論如何安排,都會有飛機不能及時降落,

思路

\(N\)最大只有\(10\),最多10組測驗組數,可以暴搜列舉所有方案

  • 優化:若搜索到一種合法方案,剪枝一路回傳即可,不需要繼續搜索

代碼

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,int>PII;
const int N=12;
PII a[N];
int d[N];
bool st[N];
int n;

//代表列舉到第u層,當前飛機的降落結束時間為now
bool dfs(int u,int now)
{
	
	if(u>=n)
	{
		for(int i=0;i<n;i++)
			if(!st[i])return false;
		
		return true;
	}
	
	for(int i=0;i<n;i++)
	{
		if(!st[i])
		{
			st[i]=true;
			//如果當前飛機的最早降落時間小于等于now,并且最晚降落時間大于等于now,
			//則從當前時刻開始降落
			if(a[i].x<=now&&a[i].y>=now)
			{
				//降落結束時間now更新為now+d[i],繼續列舉下一架飛機
				if(dfs(u+1,now+d[i]))return true;
			}
			//如果當前飛機的最早降落時間大于等于now
			else if(a[i].x>=now)
			{
				//降落結束時間now更新為a[i].x+d[i],繼續列舉下一架飛機
				if(dfs(u+1,a[i].x+d[i]))return true;
			}
			st[i]=false;
		}
	}
	
	return false;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n;
		
		for(int i=0;i<n;i++)
		{
			cin>>a[i].x>>a[i].y>>d[i];
			a[i].y+=a[i].x;
		}

		memset(st,0,sizeof st);
		puts(dfs(0,0)?"YES":"NO");
	}
	
	return 0;
}

E.接龍數列

題目內容

對于一個長度為 \(K\) 的整數數列:\(A_1, A_2, ... , A_K\),我們稱之為接龍數列:當且僅當 \(A_i\) 的首位數字恰好等于 \(A_{i?1}\) 的末位數字\((2 ≤ i ≤ K)\),例如:\(12, 23, 35, 56, 61, 11\) 是接龍數列;\(12, 23, 34, 56\) 不是接龍數列,因為 \(56\) 的首位數字不等于 \(34\) 的末位數字,所有長度為 1 的整數數列都是接龍數列,現在給定一個長度為 \(N\) 的數列 \(A_1, A_2, ... , A_N\),請你計算最少從中洗掉多少個數,可以使剩下的序列是接龍序列?

輸入格式

第一行包含一個整數 \(N\)
第二行包含 \(N\) 個整數 \(A_1, A_2, ... , A_N\)
對于 \(20\%\) 的資料,\(1 ≤ N ≤ 20\)
對于 \(50\%\) 的資料,\(1 ≤ N ≤ 10000\)
對于 \(100\%\) 的資料,\(1 ≤ N ≤ 10^5,1 ≤ A_i ≤ 10^9\)
所有 \(A_i\) 保證不包含前導 \(0\)

輸出格式

一個整數代表答案,

輸入樣例

5
11 121 22 12 2023

輸出樣例

1

洗掉 \(22\),剩余 $ 11, 121, 12, 2023 $ 是接龍數列,

思路

線性 \(dp\),定義狀態 \(f[i]\) , 代表以 \(i\) 結尾的最長序列的長度
因此,所求的最少洗掉數的個數 = $n - $最長接龍序列的長度

代碼

#include<bits/stdc++.h>
using namespace std;
const int N=12;
int f[N];
int main()
{
	int n;
	cin>>n;
	
	int maxv=0;
	for(int i=0;i<n;i++)
	{
		string s;
		cin>>s;
		int a=s[0]-'0',b=s.back()-'0';
		f[b]=max(f[b],f[a]+1);//接到前面以a結尾的數后面;或者替換掉前面一個以b結尾的數,保持不變
		maxv=max(maxv,f[b]);//更新最大值
	}
	
	cout<<n-maxv<<endl;
	
	return 0;
}

F.島嶼數量

題目內容

小藍得到了一副大小為 \(M × N\) 的格子地圖,可以將其視作一個只包含字符\(0\)(代表海水)和 \(1\)(代表陸地)的二維陣列,地圖之外可以視作全部是海水,每個島嶼由在上/下/左/右四個方向上相鄰的 \(1\) 相連接而形成,在島嶼 A 所占據的格子中,如果可以從中選出 \(k\) 個不同的格子,使得他們的坐標能夠組成一個這樣的排列:\((x_0, y_0),(x_1, y_1), . . . ,(x_{k?1}, y_{k?1})\),其中 \((\) \(x_{(i+1)\%k}, y_{(i+1)\%k}\) \()\) 是由 \((x_i, y_i)\) 通過上/下/左/右移動一次得來的 \((0 ≤ i ≤ k ? 1)\),此時這 \(k\) 個格子就構成了一個 “環”,如果另一個島嶼 \(B\) 所占據的格子全部位于這個 “環” 內部,此時我們將島嶼 B 視作是島嶼 \(A\) 的子島嶼,若 \(B\)\(A\) 的子島嶼,\(C\) 又是 \(B\) 的子島嶼,那 \(C\) 也是 \(A\) 的子島嶼,請問這個地圖上共有多少個島嶼?在進行統計時不需要統計子島嶼的數目,

輸入格式

第一行一個整數 \(T\),表示有 \(T\) 組測驗資料,
接下來輸入 \(T\) 組資料,對于每組資料,第一行包含兩個用空格分隔的整數 \(M、N\) 表示地圖大小;接下來輸入 \(M\) 行,每行包含 \(N\) 個字符,字符只可能是 \(0\)\(1\)

輸出格式

對于每組資料,輸出一行,包含一個整數表示答案,

輸入樣例

2
5 5
01111
11001
10101
10001
11111
5 6
111111
100001
010101
100001
111111

輸出樣例

1
3

對于第一組資料,包含兩個島嶼,下面用不同的數字進行了區分:

01111
11001
10201
10001
11111

島嶼 \(2\) 在島嶼 \(1\) 的 “環” 內部,所以島嶼 \(2\) 是島嶼 \(1\) 的子島嶼,答案為 \(1\)
對于第二組資料,包含三個島嶼,下面用不同的數字進行了區分:

111111
100001
020301
100001
111111

注意島嶼 \(3\) 并不是島嶼 \(1\) 或者島嶼 \(2\) 的子島嶼,因為島嶼 \(1\) 和島嶼 \(2\) 中均沒有“環”,
對于 \(30\%\) 的評測用例,\(1 ≤ M, N ≤ 10\)
對于 \(100\%\) 的評測用例,\(1 ≤ T ≤ 10,1 ≤ M, N ≤ 50\)

思路

兩次寬搜,從 \((1,1)\) 處存圖

  • 第一次寬搜,先從 \((0,0)\) ,即海水處開始向八個方向搜索,將能搜索到的 \(0\) 標記成 \(\#\),將每塊島嶼分隔開
  • 第二次寬搜,遍歷 \(g[][]\) 陣列,當遇到 \(1\) 的時候,將 \(1\) 包圍的整塊區域標記成 \(\#\),同時要統計的島嶼個數加一

代碼

#include<bits/stdc++.h>
#define x first 
#define y second
using namespace std;
typedef pair<int,int>PII;
const int N=55;
char g[N][N];
int n,m;
int dx[]={-1,0,1,0,-1,-1,1,1};
int dy[]={0,1,0,-1,-1,1,1,-1};

//分隔島嶼
void bfs1(int x,int y)
{
	queue<PII>q;
	
	q.push({0,0});
	g[0][0]='#';
	while(q.size())
	{
		auto t=q.front();
		q.pop();
		
		for(int i=0;i<8;i++)
		{
			int a=t.x+dx[i],b=t.y+dy[i];
			if(a<0||a>n+1||b<0||b>m+1||g[a][b]=='1'||g[a][b]=='#')continue;
			g[a][b]='#';
			q.push({a,b});
		}
	}
}

//將整塊島嶼標記
void bfs2(int x,int y)
{
	queue<PII>q;
	
	q.push({x,y});
	g[x][y]='#';
	while(q.size())
	{
		auto t=q.front();
		q.pop();
		
		for(int i=0;i<4;i++)
		{
			int a=t.x+dx[i],b=t.y+dy[i];
			if(a<1||a>n||b<1||b>m||g[a][b]=='#')continue;
			g[a][b]='#';
			q.push({a,b});
		}
	}
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		memset(g,'0',sizeof g);
		for(int i=1;i<=n;i++)cin>>g[i]+1;
		
		
		int x,y;
		bfs1(x,y);
			
		int cnt=0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				if(g[i][j]=='1')
					bfs2(i,j),cnt++;//統計個數
			
		cout<<cnt<<endl;
	}
	
	return 0;
}

G.子串簡寫

題目內容

程式猿圈子里正在流行一種很新的簡寫方法:
對于一個字串,只保留首尾字符,將首尾字符之間的所有字符用這部分的長度代替,
例如 \(internationalization\) 簡寫成 \(i18n,Kubernetes\) 簡寫成 \(K8s, Lanqiao\) 簡寫成 \(L5o\) 等,
在本題中,我們規定長度大于等于 \(K\) 的字串都可以采用這種簡寫方法,
長度小于 \(K\) 的字串不允許使用這種簡寫,
給定一個字串 \(S\) 和兩個字符 \(c_1\)\(c_2\)
請你計算 \(S\) 有多少個以 \(c_1\) 開頭 \(c_2\) 結尾的子串可以采用這種簡寫?

輸入格式

第一行包含一個整數 \(K\)
第二行包含一個字串 \(S\) 和兩個字符 \(c_1\)\(c_2\)
對于 \(20\%\) 的資料,\(2 ≤ K ≤ |S| ≤ 10000\)
對于 \(100\%\) 的資料,\(2 ≤ K ≤ |S| ≤ 5 × 10^5\)
\(S\) 只包含小寫字母,\(c_1\)\(c_2\) 都是小寫字母,
\(|S|\) 代表字串 \(S\) 的長度,

輸出格式

一個整數代表答案

輸入樣例

4
abababdb a b

輸出樣例

6

符合條件的子串如下所示,中括號內是該子串:
\([abab]abdb\)
\([ababab]db\)
\([abababdb]\)
\(ab[abab]db\)
\(ab[ababdb]\)
\(abab[abdb]\)

思路

先求出 \(c_1\) 的前綴和陣列 \(s[i]\) ,統計\(c_1\)在前綴中出現的次數,接著遍歷字串,每遇到一次 \(c_2\) ,就加上 \(s[i-k+1]\) ,即加上以 \(c_1\) 開頭 \(c_2\) 結尾且長度大于等于 \(K\) 的字串,最后得到答案,
\(Tips:\)注意最后答案可能很大,要開 \(longlong\)

代碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+10;
char str[N];
ll cnt[N];
char st,ed;
int k;

int main()
{
	cin>>k;
	
	cin>>str+1>>st>>ed;
	
	int n=strlen(str+1);
	//統計c1的前綴和
	for(int i=1;i<=n;i++)
		if(str[i]==st)cnt[i]=cnt[i-1]+1;
		else cnt[i]=cnt[i-1];
	
	
	//累加求個數
	ll res=0;
	for(int i=k;i<=n;i++)
		if(str[i]==ed)res+=cnt[i-k+1];
	
	
	cout<<res<<endl;
	
	return 0;
}

H.整數洗掉

題目內容

給定一個長度為 \(N\) 的整數數列:\(A_1, A_2, . . . , A_N\) ,你要重復以下操作 \(K\) 次:
每次選擇數列中最小的整數(如果最小值不止一個,選擇最靠前的),將其洗掉,并把與它相鄰的整數加上被洗掉的數值,輸出 \(K\) 次操作后的序列,

輸入格式

第一行包含兩個整數 \(N\)\(K\)
第二行包含 \(N\) 個整數,\(A_1, A_2, A_3, . . . , A_N\)

輸出格式

輸出 \(N ? K\) 個整數,中間用一個空格隔開,代表 \(K\) 次操作后的序列,

輸入樣例

5 3
1 4 2 8 7

輸出樣例

17 7

數列變化如下,中括號里的數是當次操作中被選擇的數:

[1] 4 2 8 7
5 [2] 8 7
[7] 10 7
17 7

對于 \(20\%\) 的資料,\(1 ≤ K < N ≤ 10000\)
對于 \(100\%\) 的資料,\(1 ≤ K < N ≤ 5 × 10^5,0 ≤ A_i ≤ 10^8\)

思路

題目關鍵是洗掉數列最小值,并且動態維護最小值,若取出最小元素的值比原來有變化,要重新放入優先佇列中判斷;否則就將其洗掉

  • 洗掉操作考慮使用雙鏈表,進行 \(O(1)\) 洗掉
  • 最小值利用優先佇列去維護

代碼

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N=5e5+10;
typedef long long ll;
typedef pair<ll,int>PII;
ll e[N],l[N],r[N];

priority_queue<PII,vector<PII>,greater<PII>>q;

int n,k;

//雙鏈表洗掉
void dele(int k)
{
	l[r[k]]=l[k];
	r[l[k]]=r[k];
	e[l[k]]+=e[k];
	e[r[k]]+=e[k];
	
}

int main()
{
	cin>>n>>k;
	
	//雙鏈表的頭尾
	r[0]=1,l[n+1]=n;
	
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		e[i]=x,l[i]=i-1,r[i]=i+1;
		q.push({e[i],i});//優先佇列,小根堆,儲存值和編號
	}
	
	while(k--)
	{
		auto t=q.top();
		q.pop();
		
		//取出最小元素,如果值有變化,重新放入優先佇列中;否則將其洗掉
		if(t.x!=e[t.y])q.push({e[t.y],t.y}),k++;
		else dele(t.y);	
	}
	
	for(int i=r[0];i!=n+1;i=r[i])
		cout<<e[i]<<' ';
	
	
	return 0;
}

I.景區導游

題目內容

某景區一共有 \(N\) 個景點,編號 \(1\)\(N\),景點之間共有 \(N ? 1\) 條雙向的擺渡車線路相連,形成一棵樹狀結構,在景點之間往返只能通過這些擺渡車進行,需要花費一定的時間,
小明是這個景區的資深導游,他每天都要按固定順序帶客人游覽其中 \(K\) 個景點:\(A_1, A_2, . . . , A_K\) ,今天由于時間原因,小明決定跳過其中一個景點,只帶游客按順序游覽其中 \(K ? 1\) 個景點,具體來說,如果小明選擇跳過 \(A_i\),那么他會按順序帶游客游覽 $ A_1, A_2, . . . , A_{i?1}, A_{i+1}, . . . , A_K, (1 ≤ i ≤ K) $,
請你對任意一個 \(A_i\),計算如果跳過這個景點,小明需要花費多少時間在景點之間的擺渡車上?

輸入格式

第一行包含 \(2\) 個整數 \(N\)\(K\)
以下 \(N ? 1\) 行,每行包含 \(3\) 個整數 \(u, v\)\(t\),代表景點 \(u\)\(v\) 之間有擺渡車線路,花費 \(t\) 個單位時間,
最后一行包含 \(K\) 個整數 \(A_1, A_2, . . . , A_K\) 代表原定游覽線路,

輸出格式

輸出 \(K\) 個整數,其中第 \(i\) 個代表跳過 \(A_i\) 之后,花費在擺渡車上的時間,

輸入樣例

6 4
1 2 1
1 3 1
3 4 2
3 5 2
4 6 3
2 6 5 1

輸出樣例

10 7 13 14

原路線是 \(2 → 6 → 5 → 1\)
當跳過 \(2\) 時,路線是 \(6 → 5 → 1\),其中 \(6 → 5\) 花費時間 \(3 + 2 + 2 = 7,5 → 1\) 花費時間 \(2 + 1 = 3\),總時間花費 \(10\)
當跳過 \(6\) 時,路線是 \(2 → 5 → 1\),其中 \(2 → 5\) 花費時間 \(1 + 1 + 2 = 4,5 → 1\) 花費時間 \(2 + 1 = 3\),總時間花費 \(7\)
當跳過 \(5\) 時,路線是 \(2 → 6 → 1\),其中 \(2 → 6\) 花費時間 \(1 + 1 + 2 + 3 = 7,6 → 1\) 花費時間 \(3 + 2 + 1 = 6\),總時間花費 \(13\)
當跳過 $1 $時,路線時 \(2 → 6 → 5\),其中 \(2 → 6\) 花費時間 \(1 + 1 + 2 + 3 = 7,6 → 5\) 花費時間 \(3 + 2 + 2 = 7\),總時間花費 \(14\)

對于 \(20\%\) 的資料,\(2 ≤ K ≤ N ≤ 10^2\)
對于 \(40\%\) 的資料,\(2 ≤ K ≤ N ≤ 10^4\)
對于 \(100\%\) 的資料,\(2 ≤ K ≤ N ≤ 10^5,1 ≤ u, v, A_i ≤ N,1 ≤ t ≤ 10^5\),保證 \(A_i\) 兩兩不同,

思路

\(LCA\)板子題,題目中是一棵樹形圖,求用時即為求樹上任意兩點間的距離,可利用\(LCA\)快速求出兩點之間的距離,求樹上兩個點距離的時候,可以預處理出每個點到根節點的距離,
兩點間最短距離公式: \(x\)\(y\) 的距離 \(= d[x]+d[y] - 2*d[lca(x,y)]\) ,本題可以先計算不跳過景點時的總用時,之后分類討論

  • 洗掉第 \(1\) 個結點時,減去 \(1 \sim 2\) 之間的用時即可
  • 洗掉第 \(k\) 個結點時,減去 \(k-1 \sim k\) 之間的用時
  • 其他情況減去 \(i-1 \sim i,i\sim i+1\) 之間的用時,并且加上 \(i-1 \sim i+1\) 之間的用時
    時間復雜度:預處理 \(O(nlogn)\) 查詢:\(O(logn)\)

代碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=N*2;
typedef long long ll;
int h[N],e[M],ne[M],w[M],idx;
int f[N][20];
int depth[N];
ll d[N];
int A[N];

void add(int a,int b,int c)
{
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}

//計算所有結點到根節點1的距離
void bfs()
{
    queue<int>q;
    depth[1]=1;
    q.push(1);
    
    while(q.size()){
        int t=q.front();
        q.pop();
        
        
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(depth[j])continue;
            q.push(j);
            
            if(depth[j])continue;
            depth[j]=depth[t]+1;
            d[j]=d[t]+w[i];
            f[j][0]=t;
            for(int k=1;k<=19;k++)
                f[j][k]=f[f[j][k-1]][k-1];
            
        }
    }
    
}


//倍增法求lca
int lca(int a,int b)
{
    if(depth[a]<depth[b])swap(a,b);
    
    for(int i=19;i>=0;i--)
        if(depth[f[a][i]]>=depth[b])
            a=f[a][i];
    
    
    if(a==b)return a;
    for(int i=19;i>=0;i--)
        if(f[a][i]!=f[b][i])
            a=f[a][i],b=f[b][i];
    
    
    return f[a][0];
}

int main()
{ 	
	
	memset(h,-1,sizeof h);
	int n,k;
   
    cin>>n>>k;
    
    for(int i=0;i<n-1;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    
    bfs();
    
    
    for(int i=1;i<=k;i++)
    	cin>>A[i];
    

    //求不洗掉結點前的總用時
    ll res=0;
    for(int i=2;i<=k;i++)
    {
    	int p=lca(A[i],A[i-1]);
		res+=d[A[i]]+d[A[i-1]]-2*d[p];
	}
	
	
    for(int i=1;i<=k;i++)
    {
    	ll dist;

		if(i==1)//洗掉第一個結點時,減去1~2之間的用時即可
		{
    		int p=lca(A[1],A[2]);
			dist=d[A[1]]+d[A[2]]-2*d[p];
			cout<<res-dist<<' ';
				
		}
		else if(i==k)//洗掉第k個結點時,減去k-1~k之間的用時
		{
			int p=lca(A[k],A[k-1]);
			dist=d[A[k]]+d[A[k-1]]-2*d[p];
			cout<<res-dist<<' ';
			
		}
		else{//其他情況減去i-1~i,i~i+1之間的用時,并且加上i-1~i+1之間的用時
			
			int p1=lca(A[i-1],A[i]);
			int p2=lca(A[i],A[i+1]);
			int p3=lca(A[i-1],A[i+1]);
			dist=d[A[i-1]]+d[A[i]]-2*d[p1]+d[A[i]]+d[A[i+1]]-2*d[p2];
			
			cout<<res-dist+d[A[i-1]]+d[A[i+1]]-2*d[p3]<<' ';
			
		}
		
	}
    
    
    return 0;
    
}

J.砍樹

題目內容

題目描述
給定一棵由 \(n\) 個結點組成的樹以及 \(m\) 個不重復的無序數對 $(a_1, b_1), (a_2, b_2), ... , (a_m, b_m) $,其中 \(a_i\) 互不相同,\(b_i\) 互不相同,\(a_i ≠ b_j(1 ≤ i, j ≤ m)\)
小明想知道是否能夠選擇一條樹上的邊砍斷,使得對于每個 \((a_i, b_i)\) 滿足 \(a_i\)\(b_i\) 不連通,
如果可以則輸出應該斷掉的邊的編號(編號按輸入順序從 \(1\) 開始),否則輸出 \(-1\)

輸入格式

輸入共 \(n + m\) 行,第一行為兩個正整數 \(n,m\)
后面 \(n ? 1\) 行,每行兩個正整數 \(x_i,y_i\) 表示第 \(i\) 條邊的兩個端點,
后面 \(m\) 行,每行兩個正整數 \(a_i,b_i\)
對于 \(30\%\) 的資料,保證 \(1 < n ≤ 1000\)
對于 \(100\%\) 的資料,保證 \(1 < n ≤ 100000,1 ≤ m ≤ n / 2\)

輸出格式

一行一個整數,表示答案,如有多個答案,輸出編號最大的一個,

輸入樣例

6 2
1 2
2 3
4 3
2 5
6 5
3 6
4 5

輸出樣例

4

斷開第 2 條邊后形成兩個連通塊:{3, 4},{1, 2, 5, 6},滿足 3 和 6 不連通,4 和 5 不連通,
斷開第 4 條邊后形成兩個連通塊:{1, 2, 3, 4},{5, 6},同樣滿足 3 和 6 不連通,4 和 5 不連通,
4 編號更大,因此答案為 4,

思路

$LCA + $樹上差分模板題,若砍掉某條邊讓這兩點不連通,那么這條邊一定是從 \(x\)\(y\) 路徑上的一點,我們可以利用樹上差分,讓 \(diff[x]+1,diff[y]+1,diff[lca(x,y)]-2\) ,最后做一遍 \(dfs\) 求和,讓從 \(x\)\(y\) 路徑的邊權值都加1,只需要從編號最大的倒序遍歷,若存在一條邊的值為 \(m\),則該邊即為所求答案,若不存在,則輸出 \(-1\)

代碼

#include<bits/stdc++.h>
#define x first
#define y second;
using namespace std;
const int N=1e5+10,M=N*2;
typedef pair<int,int>PII;
int h[N],e[M],ne[M],idx;
int depth[N];//記錄深度
int f[N][20];
int diff[N];//差分陣列
PII edge[N];//記錄每條邊的編號
int n,m;	

void add(int a,int b)
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}

void dfs(int u,int fa)
{
	depth[u]=depth[fa]+1;
	
	f[u][0]=fa;
	
	for(int i=1;i<=19;i++)
		f[u][i]=f[f[u][i-1]][i-1];
		
	for(int i=h[u];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(j!=fa)
		{
			dfs(j,u);
		}
	}
	
}

//倍增法求lca
int lca(int a,int b)
{
	if(depth[a]<depth[b])swap(a,b);
	
	for(int i=19;i>=0;i--)
		if(depth[f[a][i]]>=depth[b])
			a=f[a][i];
			
			
	
	if(a==b)return a;
	
	for(int i=19;i>=0;i--)
		if(f[a][i]!=f[b][i])
			a=f[a][i],b=f[b][i];
			
	return f[a][0];
}

//利用dfs對樹上的差分陣列求和
void dfs1(int u,int fa)
{
	for(int i=h[u];i!=-1;i=ne[i])
	{
		int j=e[i];	
		if(j!=fa)
		{
			dfs1(j,u);
			diff[u]+=diff[j];
		}
	}
}

int main()
{
	memset(h,-1,sizeof h);
	cin>>n>>m;
	
	for(int i=1;i<n;i++)
	{
		int a,b;
		cin>>a>>b;
		edge[i]={a,b};
		add(a,b),add(b,a);
		
	}
	
	dfs(1,0);
	
	for(int i=0;i<m;i++)
	{
		int a,b;
		cin>>a>>b;
		int p=lca(a,b);
		
		diff[a]++,diff[b]++;
		diff[p]-=2;
	}
	  
	dfs1(1,0);
	
	
	int res=-1;
	for(int i=n-1;i>=1;i--)
	{
		int a=edge[i].x,b=edge[i].y;
		if(depth[a]<depth[b])swap(a,b);//邊的權值保存在深度大的節點上
		if(diff[a]==m)
		{
			res=i;
			break;
		}
		
	}
	cout<<res<<endl;
	return 0;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/551108.html

標籤:其他

上一篇:李航統計學習概述

下一篇:返回列表

標籤雲
其他(158042) Python(38099) JavaScript(25390) Java(17999) C(15217) 區塊鏈(8260) C#(7972) AI(7469) 爪哇(7425) MySQL(7140) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5328) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4559) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2430) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1960) Web開發(1951) HtmlCss(1923) python-3.x(1918) 弹簧靴(1913) C++(1911) xml(1889) PostgreSQL(1873) .NETCore(1855) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 第14屆藍橋杯C++B組省賽題解(A-J)(更新完畢)

    A. 日期統計 題目內容 小藍現在有一個長度為 $100$ 的陣列,陣列中的每個元素的值都在 $0$ 到 $9$ 的范圍之內。 陣列中的元素從左至右如下所示: 5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 ......

    uj5u.com 2023-04-25 08:21:57 more
  • 李航統計學習概述

    監督學習 感知機 概念: 感知機模型的基本形式是: $f(x) = sign(w \cdot x + b)$ 其中,$x$ 是輸入樣本的特征向量,$w$ 是權值向量,$b$ 是偏置量,$w \cdot x$ 表示向量 $w$ 和 $x$ 的點積。$sign$ 函式表示符號函式,當輸入大于 0 時輸出 ......

    uj5u.com 2023-04-25 08:21:53 more
  • GPT4有那么可怕嗎?

    一封聯合信 3月22號也就是一個月前,馬斯克,對你沒聽錯,就是前幾天發射火箭失敗爆炸的那個,他聯合幾千名科學家用一封公開信請愿暫停一切大型AI實驗半年以上,這六個月的時間是用來做一份監督和規范AI發展的協議,避免AI的發展走向極端,超出人類的控制。 現在這封公開信已經有超過3萬人參與實名請愿。請愿名 ......

    uj5u.com 2023-04-25 08:21:37 more
  • CVE-2015-5254漏洞復現

    1.漏洞介紹。 Apache ActiveMQ 是美國阿帕奇(Apache)軟體基金會所研發的一套開源的訊息中間件,它支持 Java 訊息服務,集群,Spring Framework 等。Apache ActiveMQ 5.13.0之前 5.x 版本中存在安全漏洞,該漏洞源于程式沒有限制可在代理中序 ......

    uj5u.com 2023-04-25 08:21:16 more
  • [Week 18] 每日一題(C++,動態規劃,線段樹,數學)

    [Daimayuan] T1 最長公共子序列(C++,DP,二分) 給出從 $1$ 到 $n$ 的兩個排列 $P_1$ 和 $P_2$,求它們的最長公共子序列。 輸入格式 第一行是一個正整數 $n$。 接下來兩行,每行為 $n$ 個數,為自然數 $1,2,…,n$ 的一個排列。 輸出格式 一個數,即 ......

    uj5u.com 2023-04-25 08:20:58 more
  • 軟體自動化測驗初學者忠告

    題外話 測驗入門 很多受過高等教育的大學生經常問要不要去報測驗培訓班來入門測驗。 答案是否。 高等教育的合格畢業生要具備自學能力,如果你不具備自學能力,要好好地反省一下,為什么自己受了高等教育迷戀于各種入門級別的培訓?是沒有毅力還是不知道學習方法? 沒有毅力的話,要自己多看些勵志的書,多想想社會的殘 ......

    uj5u.com 2023-04-25 08:20:48 more
  • 【Lua】VSCode 搭建 Lua 開發環境

    前言 最近在找作業,基本所有的崗位都會問到 Lua(甚至拼 UI 的都要求會 Lua),咱能怎么辦呢,咱也只能學啊…… 工欲善其事,必先利其器。第一步,先來把環境配置好吧! 當前適用版本: LuaBinaries 版本:5.4.2 VSCode 版本:1.77.3 文章最近更新日期:2023.04. ......

    uj5u.com 2023-04-25 08:20:24 more
  • 云原生周刊:2023 年 Java 開發人員可以學習的 25 大技術技能

    文章推薦 2023 年 Java 開發人員可以學習的 25 大技術技能 這篇文章為 Java 開發人員提供了 2023 年需要學習的一些重要技能,這些技能涵蓋了現代 Java 開發、大資料和人工智能、安全性、分布式系統和區塊鏈、以及其他領域。Java 開發人員應該根據自己的需求和職業規劃,選擇適合自 ......

    uj5u.com 2023-04-25 08:20:06 more
  • 勁(很)霸(不)酷(好)炫(用)的NLP可視化包:Dodorio 使用指北

    朋友們,朋友們,事情是這樣的。最近心血來潮,突然想起很久以前看過的一個NLP可視化包。它的效果是下面這個樣子: 在此之前,已經有一些文章從論文的角度對這個包進行了介紹,詳情請見 推薦一個可互動的 Attention 可視化工具!我的Transformer可解釋性有救啦? 當時我第一眼就被這個包的效果 ......

    uj5u.com 2023-04-25 08:19:54 more
  • STM32HAL庫常用指令速查手冊

    STM32HAL庫常用指令速查手冊 持續更新中 GPIO HAL_GPIO_Init void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init); //功能: GPIO初始化 HAL_GPIO_DeInit void HA ......

    uj5u.com 2023-04-25 08:19:46 more