今天超級大水題,只是惋惜了我以前的關路燈題解,白寫了那麼詳細,結果考試仍是沒想起來……下邊來分析一下今天的一些題目。html
身爲火影的綱手大人,固然不能眼睜睜地看着斑等一夥人胡做非爲,木葉的全體忍者都信任身兼死神與忍者雙重身份的你,相信你能夠拯救世界,可是做爲資深忍者的卡卡西同窗向綱手大人提出建議,他想考驗你做爲忍者的基本能力---偵查。
斑在木葉周圍建設了許多聚點,每個聚點內都會藏有斑的手下。有些聚點是能夠連通的。陰險的斑把全部連通的聚點做爲他的一個基地,以便發動對木葉的總攻。卡卡西會告訴你每一個聚點的藏敵人數和聚點的連通狀況,他讓你找出包含聚點數最多的基地,與包含敵人數目最多的基地。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; }
\(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); }
\(zhclk\)已經堅信本身就是傳說中的有緣人,因而,帶着夢想,帶着希冀,帶着勇氣,來到了神蹟,尋找……
以下圖,神蹟的城堡是一個樹形的結構,共有 \(n\)間屋子。每間屋子都有一把鎖,而且每間屋子最多能夠到另外的兩個屋子裏(它是一棵二叉樹)。在城堡的每一個房間都存在着不一樣的寶藏。如今 \(zhclk\) 站在城堡的大門口(\(1\) 號屋子門口)擁有 \(k\)把萬能鑰匙,能夠打開任意一把鎖,但每把鑰匙只能用一次,鑰匙是拔不出來的。
問題哪有那麼簡單……,\(Zhclk\)還有一個傳送門,能夠在任什麼時候候帶他去任何一間屋子,但傳送門也只能 使用一次。
地圖上畫出了寶藏的分佈,只有得到最大價值的寶藏 \(zhclk\)的目的才能實現。
第一行:兩個數 \(n\)和\(k\)。爲城堡的屋子總數和你擁有的萬能鑰匙數。
第二行到第 \(n\)行:每行兩個數 \(x_1\) 和 \(x_2\),爲樹上的 \(n−1\) 條邊。(樹保證以 \(1\)爲根節點)。
第 \(n+1\)行:\(n\) 個數,第 \(i\) 個數爲房間 \(i\) 的寶藏價值 \(v_i\) 。
一個數,爲最大寶藏價值 \(maxv\)。
8 4
1 2
1 3
2 4
2 5
3 6
3 7
6 8
2 5 1 4 6 1 1 10
27
用鑰匙依次開\(1,2,4,5\)號房間,再用傳送門去 \(8\) 號房間,\(27=2+5+6+4+10\)
題目中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;//輸出最大值 }
這個題的名字真的是沒誰了,也不知道誰想的。原題(雖然當時我忘了),具體分析見以前個人博客:關路燈。
在一個數軸上,有 \(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\)的最小值。
第一行包含一個整數 \(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\) 。
輸出只有一行:一個整數,即消耗 \(rp\)之和的最小值。
結果不超過 \(10^9\) 。
4
3
2 2
5 8
6 1
8 7
56