集訓模擬賽7

前言

今天超級大水題,只是惋惜了我以前的關路燈題解,白寫了那麼詳細,結果考試仍是沒想起來……下邊來分析一下今天的一些題目。html

No.1 偵查

題目描述

身爲火影的綱手大人,固然不能眼睜睜地看着斑等一夥人胡做非爲,木葉的全體忍者都信任身兼死神與忍者雙重身份的你,相信你能夠拯救世界,可是做爲資深忍者的卡卡西同窗向綱手大人提出建議,他想考驗你做爲忍者的基本能力---偵查。
斑在木葉周圍建設了許多聚點,每個聚點內都會藏有斑的手下。有些聚點是能夠連通的。陰險的斑把全部連通的聚點做爲他的一個基地,以便發動對木葉的總攻。卡卡西會告訴你每一個聚點的藏敵人數和聚點的連通狀況,他讓你找出包含聚點數最多的基地,與包含敵人數目最多的基地。c++

輸入格式

\(n,m\)(\(n\) 爲據點數,聚點編號爲\(1...n,m\)爲邊數,\(n,m \le 500\));
接下的一行爲\(n\)個整數,爲每一個聚點的藏敵人數,用空格相隔,敵數\(\le 1000\)
接下來\(m\)行,每行兩個數\(u,v\),表示\(u\)\(v\)有邊相連。數組

輸出格式

第一行爲包含聚點數最多的基地內的聚點編號,以升序輸出。
第二行爲藏敵人數最多的基地內的聚點編號,以升序輸出。
注意:若求得的兩個基地包含的聚點數相同或藏敵數相同,則輸出字典序最小的。spa

樣例

樣例輸入

12 11
10 11 2 3 4 5 1 1 1 1 1 1
1 2
2 3
1 3
4 5
5 6
6 7
8 9
9 12
11 12
10 11
8 10code

樣例輸出

8 9 10 11 12
1 2 3htm

分析

其實沒啥好分析的,直接雙向建邊\(Tarjan\)求強聯通份量,而後記錄一下每一個份量的人數和大小就行blog

代碼

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int head[maxn];
vector<int>scc[maxn];
struct Node{
	int v,next;
}e[maxn<<1];
int c[maxn];
int n,m,val[maxn];
int siz[maxn];
int sum[maxn],jl1[maxn],jl2[maxn];
int dfn[maxn],low[maxn],num,tot,cnt;
int sta[maxn],top;
int vis[maxn];
void Add(int x,int y){
	e[++tot].v = y;
	e[tot].next = head[x];
	head[x] = tot;
}
void Tarjan(int x){
	dfn[x] = low[x] = ++num;
	vis[x] = 1;
	sta[++top] = x;
	for(int i=head[x];i;i=e[i].next){
		int v = e[i].v;
		if(!dfn[v]){
			Tarjan(v);
			low[x] = min(low[x],low[v]);
		}
		else if(vis[v]){
			low[x] = min(low[x],dfn[v]);
		}
	}
	if(dfn[x] == low[x]){
		cnt++;
		int y;
		while(1){
			y = sta[top--];
			c[y] = cnt;
			siz[cnt]++;
			sum[cnt]+=val[y];
			vis[y] = 0;
			scc[cnt].push_back(y);
			if(x == y)break;
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&val[i]);
	}
	for(int i=1;i<=m;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		Add(x,y);
		Add(y,x);
	}
	for(int i=1;i<=n;++i){
		if(!dfn[i])Tarjan(i);
	}
	int jlsum,jljud;
	int maxsum = 0;
	int maxjud = 0;
	for(int i=1;i<=cnt;++i){
		if(maxsum<sum[i]){
			jlsum = i;
			maxsum = sum[i];
		}
		if(maxjud<scc[i].size()){
			jljud = i;
			maxjud = scc[i].size();
		}
	}
	int dd = 0,ff = 0;
	for(int i=0;i<scc[jlsum].size();i++){
		jl1[++dd] = scc[jlsum][i];
	}
	for(int i=0;i<scc[jljud].size();i++){
		jl2[++ff] = scc[jljud][i];
	}
	sort(jl1+1,jl1+dd+1);
	sort(jl2+1,jl2+ff+1);
	for(int i=1;i<=ff;i++){
		printf("%d ",jl2[i]);
	}
	printf("\n");
	for(int i=1;i<=dd;++i){
		printf("%d ",jl1[i]);
	}
	printf("\n");
	return 0;
}

No.2 借書

問題描述

\(Dilhao\)一共有\(n\)本教科書,每本教科書都有一個難度值,他每次出題的時候都會從其中挑兩本教科書做爲借鑑,若是這兩本書的難度相差越大,\(Dilhao\)出的題就會越複雜,也就是說,一道題的複雜程度等於兩本書難度差的絕對值。排序

此次輪到\(ldxxx\)出題啦,他想要管\(Dilhao\)\(m\)本書做爲參考去出題,\(Dilhao\)想知道,若是\(ldxxx\)\(Dilhao\)給出的\(m\)本書裏挑選難度相差最小的兩本書出題,那麼\(ldxxx\)出的題複雜程度最大是多少?遞歸

輸入格式

第一行是\(n\)\(m\)ci

接下來的\(n\)行,每行一個整數\(a_i\)表示第\(i\)本書的難度。

輸入格式

一個整數爲\(ldxxx\)出的題複雜程度的最大值。

輸入樣例

6 3

5

7

1

17

13

10

輸出樣例

7

樣例解釋

\(Dilhao\)給了\(ldxxx\)難度爲\(1,10,17\)的三本書,\(ldxxx\)挑選難度爲\(10\)\(17\)的兩本書,出題複雜度爲\(7\)

若是\(Dilhao\)給出其餘任何三本書,其中的兩本書難度差的最小值都小於\(7\),因此\(ldxxx\)出題最大的複雜程度爲\(7\)

數聽說明

對於 \(30\%\)的數據: \(2\le n\le 20\)

對於 \(60\%\)的數據: \(2\le n\le 1000\)

對於 \(100\%\)的數據: \(2\le n\le 100000\)\(2 \le m\le n\)\(0\le a_i \le 1000000000\)

分析

看到標誌性的最小中的最大,(有得題是最大中的最小)確定就是二分答案了,主要就是判斷。
由於是要求出差值的最大,因此咱們就使用差分數組\(cf\)來記錄排序後的兩兩之間的難度差。而後判斷一下可否選出來\(m\)個就行了。下邊看代碼

代碼

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int Max,a[maxn];
int n,m;
int cf[maxn];//差分數組
bool check(int x){
	int cnt = 0,ch = 0;//cnt爲幾本書,ch爲最大差值
	for(int i=1;i<=n;++i){//枚舉每一本書
		ch += cf[i];//累加差值
		if(ch>=x){//差值比當前掃描到的答案大就書加一,差值置爲0
			cnt++;
			ch = 0;
		}
	}
	if(cnt>=m-1)return true;//最終可以選出m個書就爲真
	return false;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		Max = max(Max,a[i]);//記錄最大值
	}
	sort(a+1,a+n+1);//排序
	for(int i=1;i<=n;++i){//記錄差值
		cf[i] = a[i+1]-a[i];
	}
	int l=0,r=Max;//從0到最大值二分
	while(l<=r){
		int mid = (l+r)>>1;
		if(r-l == 1){//只差一就判斷一下,否則會死循環
			if(check(r) == true){//r能行就變成把l變成r,由於最後答案爲l
				l = r;
			}
			break;
		}
		if(check(mid) == true){//中間符合要求就向右轉移
			l = mid;
		}
		else r=mid;//不然向左
	}
	printf("%d\n",l);
}

No.3 搜城探寶

題目

\(zhclk\)已經堅信本身就是傳說中的有緣人,因而,帶着夢想,帶着希冀,帶着勇氣,來到了神蹟,尋找……

以下圖,神蹟的城堡是一個樹形的結構,共有 \(n\)間屋子。每間屋子都有一把鎖,而且每間屋子最多能夠到另外的兩個屋子裏(它是一棵二叉樹)。在城堡的每一個房間都存在着不一樣的寶藏。如今 \(zhclk\) 站在城堡的大門口(\(1\) 號屋子門口)擁有 \(k\)把萬能鑰匙,能夠打開任意一把鎖,但每把鑰匙只能用一次,鑰匙是拔不出來的。

問題哪有那麼簡單……,\(Zhclk\)還有一個傳送門,能夠在任什麼時候候帶他去任何一間屋子,但傳送門也只能 使用一次。

地圖上畫出了寶藏的分佈,只有得到最大價值的寶藏 \(zhclk\)的目的才能實現。

Input

第一行:兩個數 \(n\)\(k\)。爲城堡的屋子總數和你擁有的萬能鑰匙數。
第二行到第 \(n\)行:每行兩個數 \(x_1\)\(x_2\),爲樹上的 \(n−1\) 條邊。(樹保證以 \(1\)爲根節點)。
\(n+1\)行:\(n\) 個數,第 \(i\) 個數爲房間 \(i\) 的寶藏價值 \(v_i\)

Output

一個數,爲最大寶藏價值 \(maxv\)

Sample Input

8 4
1 2
1 3
2 4
2 5
3 6
3 7
6 8
2 5 1 4 6 1 1 10

Sample Output

27

Hint

用鑰匙依次開\(1,2,4,5\)號房間,再用傳送門去 \(8\) 號房間,\(27=2+5+6+4+10\)

數據範圍: \(n\le 20\)

分析

題目中u有個傳送門,因此裸的樹規確定是要爆掉的。假設使用傳送門從 \(x\)\(y\),那麼就有下邊的結論:

\(y\)僅限於沒有訪問過的節點,固然更不是 \(x\)的祖先。能夠將 \(y\)從整棵樹中獨立出來求值,而且這樣作是正確的。
能夠規定傳送到 \(y\)以後不能再往祖先方向走,也就是把這部分斷開。在 \(x\)節點使用傳送門至關於回到 \(x\) 的任意一個祖先以後再使用傳送門。所以,能夠創建一個虛根\(n+1\)使用傳送門,再令\(1\)\(n+1\)的左兒子,那麼整棵樹(除去\(y\))的值就都好計算了。
既然要把 \(y\)獨立出去計算其值,那麼能夠令\(y\)\(n+1\)的右兒子。

有了以上結論,很容易就能夠開展樹規了。
首先令 \(n+1\)的左兒子爲 \(1\),而後從 \(2\)\(n\)枚舉 \(y\) (也就是傳送到的節點),把 \(y\) 設置爲 \(n+1\)的右兒子,對樹\(n+1\)進行一次樹形\(dp\)
其中還有許多小細節,代碼註釋見

代碼

#include<bits/stdc++.h>
using namespace std;
const int maxn = 20+5;
int n,k,a[maxn],ans = 0;
int fa[maxn],ls[maxn],rs[maxn];
int dfs(int x,int sum){//樹形dp
	if(x==0)return 0;//沒有點就返回0
	if(sum==1)return a[x];//就一個鑰匙了就返回權值,由於咱們開一個傳送門的時候加了一個邊,也就是加了一個鑰匙。
	int now=0;//答案值
	for(int i=0;i<sum;++i)
		now=max(now,dfs(ls[x],i)+dfs(rs[x],sum-1-i)+a[x]);//從左兒子和右兒子遞歸
	return now;
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<n;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		if(!ls[x])ls[x] = y;//第一個點爲左兒子
		else rs[x] = y;
		fa[y] = x;//記錄每一個點的父親節點
	}
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
	}
	ls[n+1]=1;//虛根n+1
	for(int i=2;i<=n;++i){
		rs[n+1] = i;//一一枚舉傳送門到哪一個點,讓他爲右兒子
		if(ls[fa[i]] == i){//i父親的左兒子是i就把左邊斷開,而後樹歸
			ls[fa[i]] = 0;
			ans = max(dfs(n+1,k+2),ans);
			ls[fa[i]] = i;//此次遞歸完之後再連上
		}
		else {//右兒子跟左兒子同樣
			rs[fa[i]] = 0;
			ans = max(ans,dfs(n+1,k+2));
			rs[fa[i]] = i;
		}
	}
	cout<<ans<<endl;//輸出最大值
}

No.4 MM不哭

這個題的名字真的是沒誰了,也不知道誰想的。原題(雖然當時我忘了),具體分析見以前個人博客:關路燈。

題目

在一個數軸上,有 \(n\)\(MM\) 在哭泣(5555~一直哭)。

\(tcboy\)也在這個數軸上,並剛好看到了這一幕,因爲每一個\(MM\)哭都會讓\(tcboy\)損失必定的\(rp\),因而\(tcboy\)有必要去安慰她們(真命苦啊T.T)。

開始時,\(tcboy\)站在 \(k\)\(MM\)的旁邊。

如今知道第 \(i\)\(MM\) 哭泣每秒鐘會使\(tcboy\) 下降 \(w_i\)\(rp\) (單位 \(rp\)/\(s\))。而 \(tcboy\)的行走速度很慢,只有\(1m\)/\(s\)\(tcboy\) 安慰 \(MM\)的方式很特別,不須要花費時間。請計算\(tcboy\)安慰完全部 \(MM\),會消耗掉的 \(rp\)的最小值。

Input

第一行包含一個整數 \(N,2\le N\le 1000\),表示 \(MM\)的數量。
第二行包含一個整數 \(V,1\le V\le N\),表示開始時 \(tcboy\) 站在幾號 \(MM\)的旁邊。
接下來的 \(N\)行中,每行包含兩個用空格隔開的整數 \(D\)\(W\),用來描述每一個 \(MM\),其中\(0\le D\le 1000,0\le W\le 1000\)\(D\) 表示 \(MM\) 在數軸上的位置(單位: \(m\)),\(W\) 表示每秒鐘會使 \(tcboy\) 下降\(W\)\(rp\)

Output

輸出只有一行:一個整數,即消耗 \(rp\)之和的最小值。
結果不超過 \(10^9\)

Sample Input

4
3
2 2
5 8
6 1
8 7

Sample Output

56

分析

以前博客連接:http://www.javashuo.com/article/p-qthqouzu-kz.html

相關文章
相關標籤/搜索