【6.20校內test】

反正考的不是很好吧,趕腳炸了啊qwqhtml

而後這兩天一直在忙一些神奇的事情,因此沒有整理完node

手動@water_liftios

T1:大美江湖:c++

【題目背景】ide

細雪飄落長街,楓葉紅透又一年函數

不僅爲故友流連,其實我也戀長安優化

聽門外足音慢,依稀見舊時容顏this

故事幾經悲歡,結局都與你有關spa

——銀臨《大美江湖》 【問題描述】code

扶蘇聽着《大美江湖》,在劍三裏控制着他的人物炮姐來到了長安。

長安城中有一個任務,須要扶蘇進入地下的機關道,機關道是一個 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 表明此處爲怪物,炮姐會損失相應血量

每隻怪物都有三個參數來描述他們的屬性,分別是血量 HPenemy,攻擊力 STenemy,防 御力 DEenemy。且全部怪物的屬性都相同。

一旦走到怪物格,遭遇戰將開始。扶蘇必定會打死怪物,怪物對炮姐形成的傷害爲:

(忍不住說這個數據有點水,我看錯括號代碼鍋掉了竟然還A了qwq)

其中 max(a,b) 表明取 a 和 b 的最大值;的值爲不小於 x 的最小整數;下標爲 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】

5 5

MMMMM

RRRRR

QQQQQ

YYYYY

.....

5 5 5

5 1

10 10

8

2 3

1

2 3

2 3

2 3

1

2 2

1

【輸出樣例#1】

0 10 15

1 15 15

2 15 15

注意:若是屢次進入同一個格子,格子上的藥水能夠重複拾取,小怪會重複出現

注意:若是在拾取藥水的時候人物損失的生命值低於10,則會將損失的生命值降至0

SOLUTION:

這道題其實只要直接模擬就好啦,而後注意到一些細節,注意動態維護傷害就好啦

而後其實本身的代碼出鍋了,因此各位大兄弟看看代碼就好啦。

對了還有ceil不要隨便用:  點這裏吧

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<bits/stdc++.h>

using namespace std;

inline int read(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int n,m,hp,st,de,sh,x,y,stm,dem,q,cz,hps,yd;
int dx[5]={0,0,0,-1,1};
int dy[5]={0,-1,1,0,0};
char a[101][101];

int sx(){
    int f=max(1,stm-de);
    int g=hp/f;
    if(hp%f!=0) g++;
    int z=max(1,g);
    int a=max(1,st-dem);
    int y=z*a;
    return y;
}

//%%%zay 
 
int main(){
    freopen("mzq.in","r",stdin);
    freopen("mzq.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            a[i][j]=getchar();
        getchar();
    }
    hp=read();st=read();de=read();
    x=read();y=read();
    stm=read();dem=read();
    q=read();
    for(int i=1;i<=q;i++){
        cz=read();
        if(cz==1)
            cout<<hps<<" "<<stm<<" "<<dem<<endl;
        else {
            yd=read();
            x+=dx[yd];
            y+=dy[yd];
            if(a[x][y]=='M'){
                hps+=sx();
            }
            if(a[x][y]=='R'){
                if(hps<10) hps=0;
                else hps-=10;
            }
            if(a[x][y]=='Q'){
                stm+=5;
            }
            if(a[x][y]=='Y'){
                dem+=5;
            }
            if(a[x][y]=='.'){
                continue;
            }
        }
    }
    return 0;
}
出鍋的 Code
#include <cmath>
#include <cstdio>
#include <algorithm>

const int maxn = 110;

char mp[maxn][maxn];

int n, m, px, py, q;

struct Character {
  int HP, ST, DE;
  int ehp, est, ede;

  void print() {
    printf("%d %d %d\n", this->HP, this->ST, this->DE);
  }

  void fight() {
    int x = int(ceil(1.0 * ehp / std::max(1, ST - ede)));
    this->HP += std::max(1, x * std::max(1, est - DE));
  }

  void update(const char x) {
    switch (x) {
      case 'R': {
        this->HP = std::max(0, this->HP - 10);
        break;
      }
      case 'Q': {
        this->ST += 5;
        break;
      }
      case 'Y': {
        this->DE += 5;
        break;
      }
      case 'M': {
        fight();
        break;
      }
    }
  }
};
Character my;

void mov(const int x);

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", mp[i] + 1);
  }
  scanf("%d%d%d", &my.ehp, &my.est, &my.ede);
  scanf("%d%d", &px, &py);
  scanf("%d%d", &my.ST, &my.DE);
  scanf("%d", &q);
  int x;
  while (q--) {
      
    scanf("%d", &x);
    if (x == 1) {
      my.print();
    } else {
      x = 0;
      scanf("%d", &x);
      mov(x);
    }
  }
  return 0;
}

void mov(const int x) {
  switch (x) {
    case 1: {
      --py;
      break;
    }
    case 2: {
      ++py;
      break;
    }
    case 3: {
      --px;
      break;
    }
    case 4: {
      ++px;
      break;
    }
    default: {
      puts("I AK IOI");
      break;
    }
  };
  my.update(mp[px][py]);
}
STD Code

而後T2:腐草爲螢

【題目背景】

纖弱的 淤泥中妖冶

頹廢在 季夏第三月

最幼嫩的新葉 連凋零都不屑

何須生離死別

——銀臨《腐草爲螢》

【問題描述】

扶蘇給了你一棵樹,這棵樹上長滿了幼嫩的新葉,咱們約定這棵樹的根是 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 號節點 的權值爲  i T

咱們定義一個幼嫩集合幼嫩指數爲集合內節點的權值和。如今扶蘇想請問你,對於 他全部可能選出的集合,這些集合的幼嫩指數之和是多少。

爲了不答案過大,請你輸出答案對 10 7 9  取模的結果。

【輸入格式】

輸入文件名爲 dzy.in。 輸入文件中有且僅有一組數據,第一行爲兩個正整數 n 和 T,節點個數和權值參 數。 下面 n-1 行,每行有兩個正整數 u, v,表明樹上有一條邊鏈接節點 u 和節點 v。

【輸出格式】

輸出文件名爲 dzy.out。 輸出一行一個正整數,表明答案對 10 7 9  取模的結果。

【輸入樣例 】

5 0

1 2

2 3

2 4

1 5

 

【輸出樣例】

16

而後是SOLUTION:

子任務 1:

只有一個點,因此只有 {1} 這一種集合,因而答案爲 1。

子任務 二、3:

爆搜,枚舉全部可能的集合,而後計算答案。(然鵝我菜到並不會爆搜qwq)

因爲每一個點只有選進集合或不選兩種可能,因此一共有 2 n 個集合,而後能夠 O(n) 的去檢驗集合是否合法,順便統計答案。因而總複雜度 O(2 n×n)。

子任務 四、5:

考慮 DP。設 fu 是以 u 爲根的子樹的答案。

first.若是 u 沒有孩子,那麼 fu = u T

second.若是 u 只有一個孩子 v,那麼要麼選 u 不選 u 的子孫,要麼不選 u。不選 u 的答案即爲 fv,選 u 的答案即爲 u T。兩種狀況加起來就是 fu

third.若是 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 子樹內全部可能集合的權值和,因此能夠發現 Σ ws= fx 。因而 x 子樹內 的集合對答案的總貢獻是 fx×gy。同理,y 子樹內的集合對答案的貢獻是 fy×gy。 因而 fu=fy×gx+fx×gy+fx+fy+uT。gu=gx×gy+gx+gy+1。時間複雜度O(n)。

子任務六、7:

考慮在遍歷子節點的時候,已經遍歷了一些子節點,如今新加入了一個子節點。 因爲新加入一個子節點與以前遍歷的子節點沒有祖前後代關係,因而能夠以前遍歷 過得子樹當作一棵子樹,而後問題就變成了子任務四、5。

須要注意的是因爲讀入規模達到了10e6左右,須要普通的讀入優化。

(因此其實難寫的是代碼)

#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];
}
偷偷放上std

而後關於讀入優化:

  1. 1e4   cin
  2. 1e5   scanf
  3. 1e6   getchar(也就是日常寫的快讀)
  4. 1e7   fread
  5. 1e8及以上 emm,再見!

T3:錦鯉抄

【題目背景】

你在塵世中展轉了千百年

卻只讓我看你最後一眼

火光描摹容顏燃盡了時間

別留我一人,形單影隻

凋零在夢境裏面。

——銀臨&雲の泣《錦鯉抄》

【問題描述】

這首歌的文案講述了這樣一個故事:

在一個兵荒馬亂的年代,有一位畫師叫淺溪,很是喜歡畫錦鯉。戰火燒到了泰 安,他的鄰居都驚慌逃命,只有他不捨得池中錦鯉沒有離開。這天晚上庭院失火, 池中的錦鯉化妖,用生命護住了畫師的平安。

注意:因爲本題題面涉及到文案故事,在下方提供了便於理解的另外一題面版本。

扶蘇被畫師和錦鯉的故事深深地打動了。爲了能讓錦鯉和畫師繼續生活在一塊兒,他 決定回到着火的庭院中滅掉大火。

畫師的庭院能夠抽象成一個有向圖,每一個點表明着一個着火的位置。爲了量化火勢 的大小,扶蘇給每一個點一個火力值,火力值越大,表明這個點的火勢越強。風助火勢, 火借風力,對於每個着火點,都有可能由於大風使得火擴散到其餘點。有向圖的每條 邊 <u,v> 表明大火是從點 u 擴散到點 v 的。須要注意的是一個點可能會擴散到不少 點,也多是由不少點的大火一塊兒擴散成的。爲了避免由於滅掉火源讓畫師發現有人在幫 他滅火,在任意時刻,扶蘇不能滅掉任何一個不被任何點所擴散的點的火。一個點的火 被滅掉後,所表明該點的火擴散的全部邊將消失。須要說明的是,雖然邊消失了,可是 該點擴散到的全部點屬性除入度之外都不會改變,更不會消失。

由於穿越的時間有限,扶蘇只能滅掉最多 k 個點的火。忙着寫題面的扶蘇沒有時間 算出他最多能撲滅多少火力值,因而他把這個問題交給了你。

 

便於理解的題面版本:

給你一張有向圖,每一個點有一個點權。你能夠任意選擇一個有入度的點,得到它的 點權並把它和它的出邊從圖上刪去。任意時刻不能選擇沒有入度的點。最多能選擇 k 個 點,求最多能得到多少點權。

【輸入格式】

輸入文件名爲 zay.in。

輸入文件中有且僅有一組數據,第一行爲三個正整數 n,m,k,表明有向圖的點數、 邊數以及最多選擇的點數。

第二行有 n 個整數,第 i 個整數表明節點 i 的火力值(點權)

下面 m 行,每行兩個正整數 u,v,表明大火是從 u 擴散到 v 的,即有向圖的邊。

【輸出格式】

輸出文件名爲 zay.out。

輸出一行一個正整數,表明答案。

【輸入樣例】

7 7 3

10 2 8 4 9 5 7

1 2

1 3

1 4

2 5

3 6

3 7

4 7

【輸出樣例】

24


 

SOLUTION:

子任務 1:

點權都是0,因而不管怎麼選答案都是 0,輸出 0 便可。

子任務 2:

爆搜,枚舉全部可能的順序,而後計算答案。

因爲保證了數據隨機,能夠在搜索的過程當中進行剪枝,效率很高。

(不會寫qwq)

子任務 3:

給出的是一個 DAG 。考慮對於一個 DAG 來講,一個良好的的性質就是在拓撲 序後面的點不管如何變化都沒法影響到前面的點。這個題也同樣。對於任意一個不 出現原圖中自己入度爲 0 的點的序列,只要按照拓撲序選點,就必定能取遍序列中 全部的點。

因而發現這張圖上入度不爲0的點事實上均可以被選擇。因而咱們把全部入度不 爲0的點排一下序,求前k個就能夠了。時間複雜度 O(nlogn)

#include<bits/stdc++.h>

using namespace std;

inline int read(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

struct node{
    int next,to;
}edge[500005];

bool cmp(int x,int y){
    return x>y;
}

int head[500005],du[500005],a[500005];

int n,m,k,cnt,u,v,ans;

void add(int from,int to){
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
    
}

int main(){
    n=read();m=read();k=read();
    for(int i=1;i<=n;i++)
      a[i]=read();
    for(int i=1;i<=m;i++){
        u=read();v=read();
        add(u,v);
        du[v]++;
    }
    for(int i=1;i<=n;i++) if(!du[i]) a[i]=0;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=k;i++)
      ans+=a[i];
    cout<<ans<<endl;
}
30pts Code

子任務 四、5:

考慮DAG的狀況放到普通有向圖上會發生什麼。

有了子任務 3 的提示,咱們能夠考慮把整個圖縮點,將其變成一個DAG來作。

對於一個DAG,顯然能夠經過子任務 3 調整順序的方式使得每一個強連通份量的選擇狀況除選點個數之外互不影響。故下面只討論一個強連通份量內部的狀況。

一個強連通份量顯然能夠看做是一棵外向樹加上不少邊獲得的。

一棵外向樹的定義:一個外向樹的任意一個節點要麼爲葉節點,要麼它與孩子 間的全部邊都是由它指向孩子。

一棵外向樹顯然是一個 DAG 。按照以前對 DAG 上狀況的說明,顯然咱們能夠 選擇除了根節點之外的任意節點。

由於一個強連通份量內部是互相連通的,因而咱們不妨欽定一個點爲根。

對於一個沒有入度的強連通份量,咱們不妨欽定點權最小的點爲根。這樣顯然 選擇的是最優的。

對於一個有入度的強連通份量,咱們不妨欽定那個有入度的點爲根。這樣在選 擇到只剩根節點的時候,由於根節點有入度,因此根節點是能夠被選擇的。因而這 個強連通份量能夠被所有選擇。這顯然是最優的。

這樣綜合上述討論,有入度的強連通份量能夠隨便選,沒有入度的強連通份量 去掉最小的點權的點。剩下貪心取前 k 個就能夠了。

進行一次 tarjan的複雜度是 O(n+m),選前 k 個點能夠排序一下。這樣總複雜 度 O(m+nlogn),指望得分 35 分。注意到複雜度瓶頸在排序上,考慮咱們只須要前 k 大而不須要具體前 k 個之間的大小關係,因而使用 std::nth_element()函數可 以將複雜度降至 O(n+m)。指望得分 40 分。注意,輸入規模到了 10 7 級別,須要 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);
  }
}
STD++

而後關於tarjan:【here】

而後關於寫的很走心的題解:

【here】

而後關於TARJAN:

  1. 它就是一個板子
  2. YY(最後)

end-

相關文章
相關標籤/搜索