NOIp膜你題 Day1html
duliu 出題人:ZAY ios
1.大美江湖
(mzq.cpp/c)c++
【題目背景】函數
細雪飄落長街,楓葉紅透又一年
不僅爲故友流連,其實我也戀長安
聽門外足音慢,依稀見舊時容顏
故事幾經悲歡,結局都與你有關
——銀臨《大美江湖》優化
【問題描述】spa
扶蘇聽着《大美江湖》,在劍三裏控制着他的人物炮姐來到了長安。
長安城中有一個任務,須要扶蘇進入地下的機關道,機關道是一個n×m 的矩形地
圖,裏面有一些怪物和藥水。扶蘇操控着炮姐在機關道中游蕩。有些時候他但願問問你
他的角色有多少攻擊力、防護力以及丟失了多少血量。
具體的,在輸入文件中會給出一個n×m 的矩形地圖,地圖中第i 行第j 列的字符
Ci,j 表明機關道中第 i 行第 j 列的元素是什麼。具體的,Ci,j € { ‘ . ’ , ‘R’ , ‘Q’ , ‘Y’ , ‘M’ }。
其中,
一、字符 . 表明此處能夠經過,且無其餘元素
二、字符 R 表明此處爲生命藥水,能夠減小炮姐10 點丟失的血量HP
三、字符 Q 表明此處爲力量藥水,能夠增長炮姐5 點攻擊力ST
四、字符 Y 表明此處爲防護藥水,能夠增長炮姐5 點防護力DE
五、字符 M 表明此處爲怪物,炮姐會損失相應血量code
每隻怪物都有三個參數來描述他們的屬性,分別是血量HPenemy,攻擊力STenemy,防
御力DEenemy。且全部怪物的屬性都相同。
一旦走到怪物格,遭遇戰將開始。扶蘇必定會打死怪物,怪物對炮姐形成的傷害爲htm
其中 max(a,b) 表明取a和b的最大值;的值爲不小於x的最小整數;下標爲blog
enemy 的參數表明怪物的參數,下標爲my 的參數表明炮姐的參數
你會收到q 次操做,每次操做要麼是一次查詢,要麼是一次移動。
對於移動,你會再得到一個數字參數,這個參數只多是1/2/3/4 其中的一個,表明
炮姐向地圖的左/右/上/下移動。排序
【輸入格式】
輸入文件名爲mzq.in。
輸入文件中有且僅有一組數據,第一行爲兩個正整數n 和m,表明地圖的大小
下面n 行,每行m 個字符,描述機關道的地圖
下面一行有三個正整數,分別表明HPenemy,STenemy,DEenemy
下面一行有兩個整數x, y,表明炮姐初始在第x 行第y 列出發。若是出發點有怪物,不發生戰鬥,若是有道具,不會將其撿拾。
下面一行給出兩個正整數,表明炮姐初始的ST 和DE。
下面一行給出一個整數q,表明操做個數
如下q行,每行首先有一個數字,若是是 1,則表明一次查詢。不然數字必定是 2,
表明炮姐的一次移動,一個空格後會給出一個數字,做爲移動的參數。
【輸出格式】
輸出文件名爲mzq.out。
對於每一個查詢,輸出一行三個用空格隔開的整數,表明炮姐損失的血量HP,當前的攻擊力ST,以及當前的防護力DE
【題解】
這道題就是一道大模擬 那你爲何拿不了滿分
可是你不能輕敵 出題人過於強大我能怎麼辦
注意:
1.下標是從 line1 lie1開始的,也就是你讀入的時候要注意
2.在你撿拾藥水的時候,若是人物損失的生命值低於10,就會降至0
撿藥水,我怎麼知道撿哪一個藥水 可是你的小夥伴們都知道
這個固然是在撿生命藥水的時候啦 我考試的時候認爲是建撿個藥水都要這樣
3.要動態維護那個小怪獸的傷害值,計算的時候注意最好不要直接用STL裏面的 ceil(上取整函數),手寫一個就行了
至於爲啥不要直接調用ceil 點這裏吧
【代碼】
這個重測了一遍 A啦
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<string> #include<cstring> #include<bits/stdc++.h> using namespace std; int n,m; char ju[101][101]; int hpm,stm,dem; int sx,sy; int hpp=0,stp,dep; int q,flag,mp,xie; int sun() { int a=max(1,stm-dep); int b=max(1,stp-dem); int p=hpm/b; if(hpm%b) p++; int k=p*a; return max(1,k); } int main() { freopen("mzq.in","r",stdin); freopen("mzq.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",ju[i]+1); scanf("%d%d%d",&hpm,&stm,&dem); scanf("%d%d",&sx,&sy); scanf("%d%d",&stp,&dep); scanf("%d",&q); for(int i=1;i<=q;i++) { scanf("%d",&flag); if(flag==1) printf("%d %d %d\n",hpp,stp,dep); else { scanf("%d",&mp); if(mp==1) sy--; if(mp==2) sy++; if(mp==3) sx--; if(mp==4) sx++; if(ju[sx][sy]=='R') { if(hpp<10) hpp=0; else hpp-=10; } if(ju[sx][sy]=='Q') stp+=5; if(ju[sx][sy]=='Y') dep+=5; if(ju[sx][sy]=='M') { xie=sun(); hpp+=xie; } } } return 0; }
PS:洛谷
請不要像我同樣傻fufu的直接把這個題代碼交上去
看題!!
2.腐草爲螢
(dzy.cpp/c)
【題目背景】
纖弱的淤泥中妖冶
頹廢在季夏第三月
最幼嫩的新葉連凋零都不屑
何須生離死別
——銀臨《腐草爲螢》
【問題描述】
扶蘇給了你一棵樹,這棵樹上長滿了幼嫩的新葉,咱們約定這棵樹的根是1,每一個節
點都表明樹上的一個葉子。
若是你不知道什麼叫樹,你能夠認爲樹是一個邊數比節點個數少1 的無向連通圖。
咱們若是約定節點u 是樹T 的根,則能夠定義一個節點v 到根的路徑爲該無向圖上u, v
兩個節點之間的簡單路徑上的節點集合(包括路徑的兩個端點)。能夠證實,這樣的簡單路
徑只有一條。
咱們定義節點x 是節點y 的祖先(x ≠ y),當且僅當x 在y 到根的路徑上。
如今扶蘇想在這棵樹上選定一個集合,將其稱之爲幼嫩集合,來比較集合中的節點
哪一個最幼嫩。注意到一旦集合中存在兩個節點u, v,使得u 是v 的祖先,那麼必定v 要比
u 更幼嫩,由於v 是在u 的枝丫上生長出來的,那麼這樣的集合就是沒有意義的。也就是
說,扶蘇所選擇的集合必定知足要求「對於任意集合中的元素對(u, v),u 不是v 的祖先」。
扶蘇其實對這些節點哪一個最幼嫩並不感興趣,也對他能選出多少集合不感興趣,因
爲這些都是爲了問你下面的問題而創造出的題目背景。
扶蘇給每一個節點都定義了一個權值,具體的,咱們會給出一個參數T,規定 i 號節點
的權值爲 iT。
咱們定義一個幼嫩集合幼嫩指數爲集合內節點的權值和。如今扶蘇想請問你,對於
他全部可能選出的集合,這些集合的幼嫩指數之和是多少。
爲了不答案過大,請你輸出答案對 109 + 7取模的結果。
【輸入格式】
輸入文件名爲dzy.in。
輸入文件中有且僅有一組數據,第一行爲兩個正整數n 和T,節點個數和權值參數。
下面n-1 行,每行有兩個正整數u, v,表明樹上有一條邊鏈接節點u 和節點v。
【輸出格式】
輸出文件名爲dzy.out。
輸出一行一個正整數,表明答案對 109 +7取模的結果。
【樣例1 解釋】
一共有10 個集合,分別爲
{1} , {2} , {3} , {4} , {5} , {2,5} , {3,4} , {3,5} , {3,4,5} , {4,5}
因爲T=0,全部節點的權值都爲1,因此幼嫩指數之和即爲集合元素個數和,
共16個。
【題解】
1.爆搜,枚舉全部可能的集合,而後計算答案。
因爲每一個點只有選進集合或不選兩種可能,因此一共有2n 個集合,而後能夠O(n) 的去檢驗集合是否合法,順便統計答案。因而總複雜度O(2n×n)。
2.DP
設 fu 是以 u 爲根的子樹的答案。
(1)若是 u 沒有孩子,那麼 fu = uT
(2)若是 u 只有一個孩子 v,那麼要麼選 u 不選 u 的子孫,要麼不選 u。
不選u的答案即爲 fv,選 u 的答案即爲uT。兩種狀況加起來就是 fu。
(3)若是 u 有兩個孩子 x , y 。考慮要麼選 u,要麼只選 x 的子樹內的元素,要麼只選 y 的子樹內的元素,要麼既選 x 內的元素又選 y 內的元素但不選 u。
前三種狀況的答案易得。如今考慮第四種狀況的答案。
設s 是x 子樹內的某個集合。考慮不管 y 的子樹內怎麼選,再加上 s 都是合法的,由於 y 和 x 之間沒有祖前後代關係且 s 在 x 以內。
設 gu 是以 u 爲根能選擇的集合個數,那麼一共有 gy 個集合選擇s 之後依舊合法
設 s 的權值和爲 ws,因而 s 的貢獻即爲 ws×gy。因爲fx 爲x 子樹內全部可能集合的權值和,因此能夠發現
因而 x 子樹內的集合對答案的總貢獻是 fx×gy。
同理,y 子樹內的集合對答案的貢獻是fy×gx。
因而 fu = fy × gx + fx × gy + fx + fy + uT。 gu = gx × gy + gx + gy + 1。時間複雜度O(n)
Ps:咱們說說這個gu是怎麼來的:
對於任意的不一樣子樹,交叉合併一下總會出現新的符合要求的集合
gu=( fx+1 )( fy+1 ) .... ( fn+1 ) //()裏邊表示選該集合或者不選
化簡一下就是上面的啦
考慮在遍歷子節點的時候,已經遍歷了一些子節點,如今新加入了一個子節點。
因爲新加入一個子節點與以前遍歷的子節點沒有祖前後代關係,因而能夠以前遍歷過得子樹當作一棵子樹,而後問題就又變成了上面所說。
須要注意的是因爲讀入規模達到了106左右,須要普通的讀入優化。
【代碼】
#include <cstdio> typedef long long int ll; const int maxn = 1000005; const int MOD = 1000000007; template <typename T> inline void qr(T &x) { char ch; do { ch = getchar(); } while ((ch > '9') || (ch < '0')); do { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } while ((ch >= '0') && (ch <= '9')); } int n, T; int MU[maxn], frog[maxn], gorf[maxn]; bool vis[maxn]; struct Edge { int v; Edge *nxt; Edge(const int _v, Edge *h) : v(_v), nxt(h) {} }; Edge *hd[maxn]; void dfs(const int u); int main() { freopen("dzy.in", "r", stdin); freopen("dzy.out", "w", stdout); qr(n); qr(T); if (T) { for (int i = 1; i <= n; ++i) { MU[i] = i; } } else { for (int i = 1; i <= n; ++i) { MU[i] = 1; } } for (int i = 1, u, v; i < n; ++i) { u = v = 0; qr(u); qr(v); hd[u] = new Edge(v, hd[u]); hd[v] = new Edge(u, hd[v]); } dfs(1); printf("%d\n", frog[1] % MOD); return 0; } void dfs(const int u) { vis[u] = true; for (auto e = hd[u]; e; e = e->nxt) if (!vis[e->v]) { int v = e->v; dfs(v); frog[u] = (frog[u] * (gorf[v] + 1ll) % MOD) + (frog[v] * (gorf[u] + 1ll) % MOD); gorf[u] = (gorf[u] + gorf[v] + (1ll * gorf[u] * gorf[v])) % MOD; } frog[u] = (frog[u] + MU[u]) % MOD; ++gorf[u]; }
3.錦鯉抄
(zay.cpp/c)
【題目背景】
你在塵世中展轉了千百年
卻只讓我看你最後一眼
火光描摹容顏燃盡了時間
別留我一人,形單影隻
凋零在夢境裏面。
——銀臨&雲の泣《錦鯉抄》
【問題描述】
這首歌的文案講述了這樣一個故事: 在一個兵荒馬亂的年代,有一位畫師叫淺溪,很是喜歡畫錦鯉。戰火燒到了泰 安,他的鄰居都驚慌逃命,只有他不捨得池中錦鯉沒有離開。這天晚上庭院失火, 池中的錦鯉化妖,用生命護住了畫師的平安。 注意:因爲本題題面涉及到文案故事,在下方提供了便於理解的另外一題面版本。 扶蘇被畫師和錦鯉的故事深深地打動了。爲了能讓錦鯉和畫師繼續生活在一塊兒,他 決定回到着火的庭院中滅掉大火。 畫師的庭院能夠抽象成一個有向圖,每一個點表明着一個着火的位置。爲了量化火勢 的大小,扶蘇給每一個點一個火力值,火力值越大,表明這個點的火勢越強。風助火勢, 火借風力,對於每個着火點,都有可能由於大風使得火擴散到其餘點。有向圖的每條 邊<u,v> 表明大火是從點u 擴散到點v 的。須要注意的是一個點可能會擴散到不少 點,也多是由不少點的大火一塊兒擴散成的。爲了避免由於滅掉火源讓畫師發現有人在幫 他滅火,在任意時刻,扶蘇不能滅掉任何一個不被任何點所擴散的點的火。一個點的火 被滅掉後,所表明該點的火擴散的全部邊將消失。須要說明的是,雖然邊消失了,可是 該點擴散到的全部點屬性除入度之外都不會改變,更不會消失。 由於穿越的時間有限,扶蘇只能滅掉最多k 個點的火。忙着寫題面的扶蘇沒有時間 算出他最多能撲滅多少火力值,因而他把這個問題交給了你。
說人話:
給你一張有向圖,每一個點有一個點權。你能夠任意選擇一個有入度的點,得到它的點權並把它和它的出邊從圖上刪去。任意時刻不能選擇沒有入度的點。最多能選擇k 個點,求最多能得到多少點權。
【輸入格式】
輸入文件名爲zay.in。
輸入文件中有且僅有一組數據,第一行爲三個正整數n,m,k,表明有向圖的點數、邊數以及最多選擇的點數。
第二行有n 個整數,第i 個整數表明節點i 的火力值(點權)
下面m 行,每行兩個正整數u,v,表明大火是從u 擴散到v 的,即有向圖的邊。
【輸出格式】
輸出文件名爲zay.out。
輸出一行一個正整數,表明答案。
【題解】
子任務3:
給出的是一個DAG 。
考慮對於一個DAG 來講,一個良好的的性質就是在拓撲序後面的點不管如何變化都沒法影響到前面的點。這個題也同樣。對於任意一個不出現原圖中自己入度爲0 的點的序列,只要按照拓撲序選點,就必定能取遍序列中全部的點。
因而發現這張圖上入度不爲0的點事實上均可以被選擇。因而咱們把全部入度不爲0的點排一下序,求前k個就能夠了。
時間複雜度O(nlogn)
子任務四、5:
考慮DAG的狀況放到普通有向圖上會發生什麼。
有了子任務3 的提示,咱們能夠考慮把整個圖縮點,將其變成一個DAG來作。
QUS:何爲縮點?就是把一個環縮成一個點,由於環裏的點均可以相互到達
對於一個DAG,顯然能夠經過子任務3 調整順序的方式使得每一個強連通份量的選擇狀況除選點個數之外互不影響。故下面只討論一個強連通份量內部的狀況。
一個強連通份量顯然能夠看做是一棵外向樹加上不少邊獲得的。
一棵外向樹的定義:一個外向樹的任意一個節點要麼爲葉節點,要麼它與孩子間的全部邊都是由它指向孩子。
一棵外向樹顯然是一個DAG 。
按照以前對DAG 上狀況的說明,顯然咱們能夠選擇除了根節點之外的任意節點。
由於一個強連通份量內部是互相連通的,因而咱們不妨欽定一個點爲根。
對於一個沒有入度的強連通份量,咱們不妨欽定點權最小的點爲根。這樣顯然選擇的是最優的。
對於一個有入度的強連通份量,咱們不妨欽定那個有入度的點爲根。這樣在選擇到只剩根節點的時候,由於根節點有入度,因此根節點是能夠被選擇的。因而這個強連通份量能夠被所有選擇。這顯然是最優的。
這樣綜合上述討論,有入度的強連通份量能夠隨便選,沒有入度的強連通份量去掉最小的點權的點。剩下貪心取前k 個就能夠了。
進行一次tarjan的複雜度是O(n+m),選前k 個點能夠排序一下。這樣總複雜度O(m+nlogn)
注意到複雜度瓶頸在排序上,考慮咱們只須要前k 大而不須要具體前k 個之間的大小關係,因而使用std::nth_element()函數能夠將複雜度降至O(n+m)。
注意,輸入規模到了107 級別,須要fread 來實現讀入優化。
【代碼】
#include <cstdio> #include <algorithm> #include <functional> #ifdef ONLINE_JUDGE #define freopen(a, b, c) #endif typedef long long int ll; namespace IPT { const int L = 1000000; char buf[L], *front=buf, *end=buf; char GetChar() { if (front == end) { end = buf + fread(front = buf, 1, L, stdin); if (front == end) return -1; } return *(front++); } } template <typename T> inline void qr(T &x) { char ch = IPT::GetChar(), lst = ' '; while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar(); while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar(); if (lst == '-') x = -x; } const int maxn = 1000006; struct Edge { int v; Edge *nxt; Edge(const int _v, Edge *h) : v(_v), nxt(h) {} }; Edge *hd[maxn]; int n, m, k, vistime, top, scnt; int MU[maxn], dfn[maxn], low[maxn], stack[maxn], belong[maxn], minv[maxn]; bool instack[maxn], haveind[maxn]; void tarjan(const int u); int main() { freopen("zay.in", "r", stdin); freopen("zay.out", "w", stdout); qr(n); qr(m); qr(k); MU[0] = 2333; for (int i = 1; i <= n; ++i) qr(MU[i]); for (int i = 1, u, v; i <= m; ++i) { u = v = 0; qr(u); qr(v); hd[u] = new Edge(v, hd[u]); } for (int i = 1; i <= n; ++i) if (!dfn[i]) { tarjan(i); } for (int u = 1; u <= n; ++u) { for (auto e = hd[u]; e; e = e->nxt) if (belong[u] != belong[e->v]) { haveind[belong[e->v]] = true; } } for (int i = 1; i <= scnt; ++i) if (!haveind[i]) { MU[minv[i]] = 0; } std::nth_element(MU + 1, MU + 1 + k, MU + 1 + n, std::greater<int>()); int ans = 0; for (int i = 1; i <= k; ++i) { ans += MU[i]; } printf("%d\n", ans); return 0; } void tarjan(const int u) { dfn[u] = low[u] = ++vistime; instack[stack[++top] = u] = true; for (auto e = hd[u]; e; e = e->nxt) { int v = e->v; if (!dfn[v]) { tarjan(v); low[u] = std::min(low[u], low[v]); } else if (instack[v]) { low[u] = std::min(low[u], dfn[v]); } } if (dfn[u] == low[u]) { int v, &_mv = minv[++scnt]; do { instack[v = stack[top--]] = false; belong[v] = scnt; if (MU[v] < MU[_mv]) _mv = v; } while (v != u); } }
【擴充知識】
1.讀入數據時應該用什麼?想用什麼用什麼
固然要參考一下數據範圍:
(1)1e4 cin
(2)1e5 scanf
(3)1e6 getchar
(4)1e7 fread
(5)1e8及以上 請把出題人從窗戶裏扔出去
2.何爲暴力?
前輩雲:暴力出奇跡,打表拿省一。
暴力分爲兩類:
(1)模擬:題目要求你幹什麼,你就幹什麼
(2)搜:DFS啊BFS 啊能搜就搜出來
3.何爲fread?
一個板子
4.何爲tarjan?
也是一個板子
超讚(我還沒看完)
5.給泥萌看個樹:
THE END
好難鴨
但這僅僅是DAY1
深深地感覺到了NOIp對個人敵意
WelcomeToNOIp2019!!!!!