2024牛客暑期多校訓練營6

Luckyblock發表於2024-08-01

目錄
  • 寫在前面
  • H
  • B
  • D
  • A
  • F
  • I
  • 寫在最後

寫在前面

比賽地址:https://ac.nowcoder.com/acm/contest/81601#question

以下按個人難度向排序。

純純戰犯場呃呃呃呃做題不看題小保底當成 100 抽一發我草太唐了開局吃五發呃呃呃呃中期口了三題出來寫出來兩道最後好歹沒太爛呃呃

置頂廣告:中南大學 ACM 集訓隊絕贊招新中!

有資訊奧賽基礎,獲得 NOIP 省一等獎並達到 Codeforces rating 1900+ 或同等水平及以上者,可以直接私聊我與校隊隊長聯絡,免選拔直接進校集訓隊參加區域賽!

沒有達到該水平但有志於 XPCX 賽事請關注每學年開始的 ACM 校隊招新喵!

到這個時候了還缺隊友實在不妙!求求求求快來個大神帶我嗚嗚嗚嗚

H

簽到。

模擬即可。

小保底是 90 發非常不行啊,不如我們馬娘和 BA 直接吃井媽的

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e6 + 10;
//=============================================================
int n, cnt[310];
std::string s;
std::vector<char> pos;
//=============================================================
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> s; 
    n = s.length();

    s = "$" + s;
    pos.clear();
    int flag = 1;

    if (n >= 10) {
      for (int i = 0; i <= 'U'; ++ i) cnt[i] = 0;
      for (int i = 1; i <= 9; ++ i) ++ cnt[(int)s[i]];
      for (int l = 1, r = 10; r <= n; ) {
        ++ cnt[(int)s[r]];
        if (cnt[(int)'3'] == 10) flag = 0;
        -- cnt[(int)s[l]];
        ++ l, ++ r;
      }
    }
    if (n >= 90) {
      for (int i = 0; i <= 'U'; ++ i) cnt[i] = 0;
      for (int i = 1; i <= 89; ++ i) ++ cnt[(int)s[i]];
      for (int l = 1, r = 90; r <= n; ) {
        ++ cnt[(int)s[r]];
        if (cnt[(int)'5'] == 0 && cnt[(int)'U'] == 0) flag = 0;
        -- cnt[(int)s[l]];
        ++ l, ++ r;
      }
    }

    for (auto c: s) if (c == '5' || c == 'U') pos.push_back(c);
    for (int i = 1, sz = pos.size(); i < sz; ++ i) {
      if (pos[i] != 'U' && pos[i - 1] != 'U') flag = 0;
    }
    std::cout << (flag ? "valid\n" : "invalid\n");
  }
  return 0;
}

B

數學,模擬。

考慮按照給定順序進行切分。發現第一刀下去會變成 2 塊,在此之後每刀經過 \(i\) 條線,答案便增加 \(i+1\)。然後手玩了下發現每刀經過的線數變化規律為:

\[(0, 1, 2, \cdots, k - 2), k - 1, k - 1, \cdots, k - 1, (k, k + 1, k + 2, \cdots, 2k - 2) \]

賽時懶了就直接模擬了,實際上可以 \(O(1)\) 算,有關平面圖的證明詳見官方題解。

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, k, ans; 
signed main(){
	cin >> n >> k;
	if(n == k * 2) {
		cout << n << endl;
		return 0;
	}
	k = min(k, n - k);
	ans = 2;
	for(int i = 1; i <= k - 1; ++ i) ans += i + 1;
	for(int i = k; i <= n - k; ++ i) ans += k - 1 + 1;
	for(int i = n - k + 1, j = 0; i <= n - 1; ++ i, ++ j) ans += k + j + 1;
	cout << ans << endl;
	return 0;
}

D

連通性問題,Tarjan

所有 Lun 都要在環中,所有 Qie 都不在環中,容易想到當刪完邊後剩下的子圖使用邊雙連通分量縮完點後一定會很好看——所有 Lun 均在邊雙中,而所有 Qie 將縮完點後的子圖連成樹狀結構。

發現可以分別處理 Lun 和 Qie。考慮僅對所有 Lun 執行邊雙連通分量演算法,保留其中所有在不小於 3 的邊雙中的 Lun,並將他們使用並查集縮成一個點。然後再列舉所有 Qie 進行加邊,在此過程中使用並查集維護連通性即可。

若最後可以得到一張連通圖則 YES,否則 NO。

//
/*
By:Luckyblock

https://www.luogu.com.cn/problem/P8436
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
const int kM = 1e6 + 10;
//=============================================================
struct Edge {int u, v;};
std::vector <Edge> qie, ans;

int n, m;
int edgenum = 1, head[kN], v[kM], ne[kM];
int dfnnum, dfn[kN], low[kN];
bool bridge[kM], inans[kM];
int dccnum, indcc[kN], bel[kN];
std::vector <int> dcc[kN];

int fa[kN];
//=============================================================
void Add(int u_, int v_) {
  v[++ edgenum] = v_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
void Tarjan(int u_, int from_) {
  dfn[u_] = low[u_] = ++ dfnnum;
  for (int i = head[u_]; i > 1; i = ne[i]) {
    int v_ = v[i];
    if (!dfn[v_]) {
      Tarjan(v_, i);
      if (low[v_] > dfn[u_]) bridge[i] = bridge[i ^ 1] = 1;
      low[u_] = std::min(low[u_], low[v_]);
    } else if (i != (from_ ^ 1)) {
      low[u_] = std::min(low[u_], dfn[v_]);
    }
  }
}
void Dfs(int u_, int id_) {
  indcc[u_] = true;
  bel[u_] = id_;
  dcc[id_].push_back(u_);
  for (int i = head[u_]; i > 1; i = ne[i]) {
    int v_ = v[i];
    if (indcc[v_] || bridge[i]) continue;
    Dfs(v_, id_);
  }
}
int find(int x_) {
  return (fa[x_] == x_) ? x_ : (fa[x_] = find(fa[x_]));
}
void merge(int x_, int y_) {
  int fx = find(x_), fy = find(y_);
  if (fx == fy) return ;
  fa[fx] = fy;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n >> m;
  for (int i = 1; i <= m; ++ i) {
    int u_, v_; std::string w_; std::cin >> u_ >> v_ >> w_;
    if (w_ == "Lun") {
      Add(u_, v_), Add(v_, u_);
    } else {
      qie.push_back((Edge) {u_, v_});
    }
  }
  for (int i = 1; i <= n; ++ i) if (!dfn[i]) Tarjan(i, 0);
  for (int i = 1; i <= n; ++ i) if (!indcc[i]) Dfs(i, ++ dccnum);

  int flag = 1;
  for (int i = 1; i <= n; ++ i) fa[i] = i;

  for (int id = 1; id <= dccnum; ++ id) {
    for (auto u_: dcc[id]) {
      for (int i = head[u_]; i > 1; i = ne[i]) {
        if (inans[i] || inans[i ^ 1] || bel[v[i]] != id) continue;
        inans[i] = inans[i ^ 1] = 1;
        ans.push_back((Edge) {u_, v[i]});
        merge(u_, v[i]);
      }
    }
  } 
  for (auto e: qie) {
    if (find(e.u) == find(e.v)) continue;
    ans.push_back(e);
    merge(e.u, e.v);
  }
  for (int i = 1; i <= n; ++ i) if (find(i) != find(1)) flag = 0;

  if (!flag) {
    std::cout << "NO\n";
  } else {
    std::cout << "YES\n";
    std::cout << ans.size() << "\n";
    for (auto e: ans) std::cout << e.u << " " << e.v << "\n";
  }
  // printf("%d\n", dccnum);
  // for (int i = 1; i <= dccnum; ++ i) {
  //   printf("%d ", dcc[i].size());
  //   for (int j = 0, sz = dcc[i].size(); j < sz; ++ j) {
  //     printf("%d ", dcc[i][j]);
  //   }
  //   printf("\n");
  // }
  return 0;
}
/*
5 6
1 2 Lun
1 3 Lun
2 3 Lun
3 4 Lun
4 5 Lun
5 3 Lun
*/

A

樹形 DP。

賽時 dztlb 大神單刷的,我看不懂他在寫啥。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const double eps=1e-10;
const int N=510000;
int T,n;
int head[N],tot;
struct node{
	int to,nxt,w;
}e[N<<1];
void add(int u,int v,int w){
	e[++tot].to=v,e[tot].nxt=head[u],head[u]=tot;
	e[tot].w=w;
}
double ans[N];
void dfs(int u,int fa,int dep,int x,int y){
	int cnt=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue;
		++cnt;
	}
	if(cnt!=0)ans[u]=0;
	else {ans[u]=(double)x/(double)(x+y); return;}
	if(dep%2==1){
		
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(v==fa) continue;
			int dx=x,dy=y;
			if(e[i].w==1) ++dx;
			else ++dy;
			dfs(v,u,dep+1,dx,dy);
			if(ans[u]<=ans[v]+eps) ans[u]=ans[v];
		}
		if((x+y)!=0) {
			if(ans[u]>=(double)x/(double)(x+y))
			ans[u]=(double)x/(double)(x+y);
		}
	}else{
		ans[u]=(double)x/(double)(x+y);
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(v==fa) continue;
			int dx=x,dy=y;
			if(e[i].w==1) ++dx;
			else ++dy;
			dfs(v,u,dep+1,dx,dy);
			if(ans[u]>=ans[v]+eps) ans[u]=ans[v];
		}
	}
}
signed main(){
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;++i){
			head[i]=0;
			ans[i]=0;
		}
		tot=0;
		for(int i=1,u,v,w;i<n;++i){
			cin>>u>>v>>w;
			add(u,v,w);
			add(v,u,w);
		}
		dfs(1,0,1,0,0);
		printf("%.12Lf\n",ans[1]);
	}
	return 0;
}
/*
1
9
1 2 1
2 3 1
3 4 0
4 5 0
5 6 0
6 7 0
7 8 1
8 9 0

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

F

圖論,構造

想了下完全圖的情況就直接秒了呃呃,然而一直被卡最後 5min 被 dztlb 大神叉掉了過了,實在刺激!!!

首先考慮 \(m=0\) 的情況,此時沒有任何限制可以隨便走;然後考慮有多棵樹的情況,發現此時對於不同樹間的節點沒有任何限制,也可以隨便走,於是僅需考慮單棵樹的情況,並將所有單棵樹構造的通路連起來即可。

對於單棵樹,發現僅有菊花圖的情況是無解的,此時僅可以將所有葉子構造成哈密爾頓路徑但會剩下一個根,除此之外一定可以構造出完整的哈密爾頓路徑;然後發現若有多棵樹,無論有多少菊花圖都有解。若其中某棵樹是菊花圖,則可先將其中所有葉子構造成一條通路,然後將剩下的根塞到到其他樹構造出的通路里,再把所有樹的通路連起來即可。

然後考慮對於某棵樹應當如何構造哈密爾頓通路:

  • 若點數為 1,此時相當於沒有限制,不看做菊花圖,通路即為自身。
  • 若為點數不小於 2 的菊花圖,則將其中所有葉子構造成一條通路,並額外記錄一下根
  • 否則直徑長度一定不小於 4,發現此時可以以直徑端點為根對樹進行分層,每層間可以隨便連,且非相鄰層也可以隨便連。於是想到奇偶分層,分別將奇數偶數層按照層數連成一條通路,並將奇數層接到偶數層後面即可。

此時所有樹都變成了一條通路,若菊花圖則外加一個根。考慮將所有菊花圖的根插入到下一棵樹的通路的最後,再將所有通路首尾相連即可。注意若最後一棵樹是菊花圖,則需要特判將最後一棵樹的菊花插到整個通路的開頭。

特判僅有一棵樹且為菊花圖時無解。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 5e5 + 10;
//=============================================================
int n, m, treenum, fa[kN], du[kN], bel[kN];
int edgenum, head[kN], v[kN << 1], ne[kN << 1]; 
bool vis[kN];
int from[kN], dis[kN];
int asshole[kN];
std::vector<int> tree[kN], seq[kN];
//=============================================================
int find(int x_) {
  return (fa[x_] == x_) ? x_ : (fa[x_] = find(fa[x_]));
}
void merge(int x_, int y_) {
  int fx = find(x_), fy = find(y_);
  if (fx == fy) return ;
  fa[fx] = fy;
}
void addedge(int u_, int v_) {
  v[++ edgenum] = v_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
void dfs(int u_, int fa_, bool flag) {
  if (flag) from[u_] = fa_; 
	dis[u_] = dis[fa_] + 1;
	for (int i = head[u_]; i; i = ne[i]) {
    int v_ = v[i];
    if (v_ == fa_) continue;
    dfs(v_, u_, flag);
  }
}
void solve(int id_) {
  int road[2] = {tree[id_][0], tree[id_][0]}, maxdis = 0;
  dfs(road[0], 0, 0);
  for (auto x: tree[id_]) if (dis[x] > maxdis) road[0] = x, maxdis = dis[x];
  dfs(road[0], 0, 1);
  maxdis = 0;
  for (auto x: tree[id_]) if (dis[x] > maxdis) road[1] = x, maxdis = dis[x];

  if (maxdis == 1) {
    seq[id_].push_back(road[0]);
    return ;

  } else if (maxdis == 2) {
    asshole[id_] = road[0];
    seq[id_].push_back(road[1]);
    return ;

  } else if (maxdis == 3) {
    for (auto x: tree[id_]) {
      if (du[x] == (int) tree[id_].size() - 1) asshole[id_] = x;
      else seq[id_].push_back(x);
    }
    return ;

  }
  
  std::vector<int> temp;
  std::queue<int> q; q.push(road[0]);
  while (!q.empty()) {
    int u_ = q.front(); q.pop();
    vis[u_] = 1;
    if (dis[u_] % 2 == 1) temp.push_back(u_);
    else seq[id_].push_back(u_);

    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i];
      if (vis[v_]) continue;
      q.push(v_);
    } 
  }
  for (auto x: temp) seq[id_].push_back(x);
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> m;
    edgenum = treenum = 0;
    for (int i = 1; i <= n; ++ i) {
      fa[i] = i; 
      du[i] = head[i] = bel[i] = asshole[i] = 0;
      dis[i] = from[i] = vis[i] = 0;
      tree[i].clear(), seq[i].clear();
    }

    for (int i = 1; i <= m; ++ i) {
      int u_, v_; std::cin >> u_ >> v_;
      addedge(u_, v_), addedge(v_, u_);
      ++ du[u_], ++ du[v_];
      merge(u_, v_);
    }
    for (int i = 1; i <= n; ++ i) {
      if (!bel[find(i)]) bel[find(i)] = ++ treenum; 
      tree[bel[find(i)]].push_back(i);
    }
    for (int i = 1; i <= treenum; ++ i) solve(i);

    if (treenum == 1 && asshole[1]) {
      std::cout << -1 << "\n";
      continue;
    }
    if (asshole[treenum]) std::cout << asshole[treenum] << " ";
    for (int i = 1; i < treenum; ++ i) if (asshole[i]) seq[i + 1].push_back(asshole[i]);
    
    for (int i = 1; i <= treenum; ++ i) {
      for (auto x: seq[i]) std::cout << x << " ";
    }
    std::cout << "\n";
  }
  return 0;
}
/*
1
3 1
2 3
*/

I

DP。

實際上 2h 就秒了這個狀態太顯然了呃呃,然而直到結束都在調程式碼紅溫於是沒時間寫呃呃呃呃,要是有三個人鐵拿下媽的


寫在最後

學到了什麼:

  • F:對情況討論分類時,應當儘可能考慮全面每種情況的具體限制,以防多加限制導致需要特判一堆不必要的東西。

結尾廣告:中南大學 ACM 集訓隊絕贊招新中!

有資訊奧賽基礎,獲得 NOIP 省一等獎並達到 Codeforces rating 1900+ 或同等水平及以上者,可以直接私聊我與校隊隊長聯絡,免選拔直接進校集訓隊參加區域賽!

沒有達到該水平但有志於 XPCX 賽事請關注每學年開始的 ACM 校隊招新喵!

到這個時候了還缺隊友實在不妙!求求求求快來個大神帶我嗚嗚嗚嗚

相關文章