1. 程式人生 > 其它 >Harbour.Space Scholarship Contest 2021-2022 題解

Harbour.Space Scholarship Contest 2021-2022 題解

上分好場,可惜就被我這麼白白錯過了/wn

多好的上分機會啊,要是換個時間(指改在 NOI 之後)我說不定就能上 2500 了(做白日夢 ing)

A

簽到題不多說,顯然只有末尾為 \(9\) 的數是 interesting 的,因此答案就是 \(\lfloor\dfrac{n+1}{10}\rfloor\)

B

暴力列舉兩個斷點然後線性地檢查一遍,時間複雜度 \(n^3\)

不知道為什麼這種題會有人 FST

C

注意到資料範圍很小,因此考慮 \(2^{10}\) 列舉所有狀態,然後暴力列舉一下取個 \(\min\) 即可。

D

Weak pretest!!!111/fn/fn

考慮貪心,假設當前 \(s\) 掃到字元 \(s_i\),目前匹配了 \(t\)

的前 \(j\) 位,那麼我們暴力跳到下一個等於 \(t_{j+1}\) 且奇偶性與 \(i\) 不同的位置 \(k\) 即可,預處理 \(nxt_{i,j}\) 表示 \(s_i\) 下一個字元 \(j\) 的位置即可實現線性求解。

時間複雜度 \(\mathcal O(26n)\)

注意事項:注意特判最後一個字元的位置與 \(n\) 的奇偶性是否相同,否則會 WA 19。

順便給出一組 hack 資料:

1
aa
a

要是在現場我就 FST 了(

E

這道題還算有點意思。

首先按照這題的結論(大霧,其實這個結論應該是人盡皆知了吧),對於某個 \(k\) 而言,對於每個 \(p_i\),如果 \(i\le k\)

,連邊 \(p_i\to n-k+i\),否則連邊 \(p_i\to i-k\),那麼將原排列進行 \(k\) cyclic shift 後變成給定的排列所需的最少步數就是 \(n\) 減去得到的圖的置換環的個數。

直接做顯然不可行,不過注意到有個條件 \(m\le\dfrac{n}{3}\),這也就意味著在合法的連邊方案中至少有 \(\dfrac{2n}{3}\) 個置換環,根據抽屜原理,大小為 \(1\) 的置換環至少有 \(\dfrac{n}{3}\) 個,因此我們考慮對於每個 \(k\),計算在對原排列進行 \(k\) cyclic shift 後得到的排列與給定排列連成的圖中有多少個大小為 \(1\)

的置換環,然後對該數目 \(\ge\dfrac{n}{3}\)\(k\) 暴力檢驗即可,顯然檢驗的 \(k\) 的數目是 \(\mathcal O(1)\) 級別的,因此總複雜度也是線性的。

tbh 我感覺這題比 F 難,可能是因為我做這種人類智慧題不太行罷。。。

F

簡單題,現場很快就想出來了可惜由於要睡覺沒時間寫了

首先咱們肯定要對於每個新加進來的 \(a_i\) 計算它與前面所有 \(a_j\) 取模得到的餘數的和唄。那咱肯定就要分為兩部分,\(\sum\limits_{j=1}^{i-1}a_i\bmod a_j\)\(\sum\limits_{j=1}^{i-1}a_j\bmod a_i\) 分別求和。首先考慮第一部分,注意到 \(a_i\bmod a_j=a_i-\lfloor\dfrac{a_i}{a_j}\rfloor·a_j\),因此 \(\sum\limits_{j=1}^{i-1}a_i\bmod a_j=\sum\limits_{j=1}^{i-1}a_i-\lfloor\dfrac{a_i}{a_j}\rfloor·a_j\),我們建一個樹狀陣列 \(T_1\),考慮在前面每加入一個 \(a_j\),就在 \(T_1\)\(a_j,2a_j,3a_j,\cdots\) 的位置上 \(+a_j\),那麼 \(T_1\)\([1,a_i]\) 部分的字首和就是 \(\sum\limits_{j=1}^{i-1}\lfloor\dfrac{a_i}{a_j}\rfloor·a_j\),然後就有手就行了。其次考慮第二部分,我們考慮列舉 \(a_i\) 的倍數 \(ka_i\),那麼對於 \(a_j\in[ka_i,(k+1)a_i),\lfloor\dfrac{a_j}{a_i}\rfloor=k\),因此我們另件兩個樹狀陣列 \(T_2,T_3\) 維護區間和和區間內數的個數,然後每次在樹狀陣列中查詢滿足 \(a_j\in[ka_i,(k+1)a_i)\)\(a_j\) 的和 \(s\)\(a_j\) 的個數 \(c\),答案加上 \(s-ck\) 即可。

時間複雜度 \(n\log^2n\),但由於常數小+TL 大可以通過,實測 4s 時限不到 1s 就跑過去了。。。

G

一道挺有意思的題,感覺這場最有含金量的題是 E 和 G(

首先考慮一個最 trivial 的情況,怎樣判斷兩個點是否在一個連通塊中。考慮這樣一個過程:將所有 \(a_i\) 分解質因數,然後將所有質因子看作一個點,那麼對於每個 \(a_i\)\(a_i\) 所有質因子合併成一個連通塊,檢驗時只需檢驗兩點是否在一個連通塊中即可。

接下來考慮原問題,注意到一個性質,就是最多兩次操作就可以搞定:如果兩個數都是偶數那直接不互質在一個連通塊中,如果兩個數一奇一偶那最多隻需把奇數擴充套件一下就能連通,如果兩個數都是奇數那最多隻需把兩個奇數分別擴充套件一下就能連通。因此我們只需再檢驗一次操作能否搞定,如果不能答案就是 \(2\)。怎麼檢驗呢?我們考慮每個連通塊進行一次擴充套件能與哪些連通塊連通,我們列舉每個數 \(a_i\),那麼顯然擴充套件 \(a_i\) 後會使 \(a_i\)\(a_i+1\) 所有質因子合併,用個 set 維護一下即可,複雜度 \(\omega^2(n)n\log n\),然鵝比 wjz \(n\log n+49n\) 跑得還快。。。((

H

降智了沒想出來/ll,然鵝看了題解後感覺不是太難(

考慮對 \(a_i\) 建立一個 \(01\)-trie,與普通的 \(01\)-trie 不同的是,該 \(01\)-trie 與線段樹有著類似的結構,每個節點表示一個區間 \([L,R]\) 並維護以下四個值:

  • 在區間 \([L,R]\) 中出現的最小的數 \(-L\)
  • 在區間 \([L,R]\) 中出現的最大的數 \(-L\)
  • 區間 \([L,R]\) 中最接近的兩個數的差
  • 區間 \([L,R]\) 的長度 \(R-L+1\)

那麼對於某個 \(k\) 而言,其答案就是對於所有 \(b\) 滿足 \(k\)\(2^b\) 位為 \(1\),將 \(01\)-trie 自下而上的第 \(b+1\) 層所有節點的左右兒子交換後,根節點的答案。直接換顯然工作量太大,穩穩地 T 掉。不過注意到本題的 01-trie 與線段樹一樣有一個性質,那就是所有節點表示的區間的長度之和是 \(2^kk\) 級別的,因此考慮對所有節點開一個長度 \(R-L+1\) 的陣列,第 \(x\) 位表示將該區間中的所有數異或 \(x\) 後該節點上的資訊,對於每一位顯然可以 \(\mathcal O(1)\) 上推資訊,因此總複雜度 \(2^kk\)

const int MAXN=1<<20;
const int INF=0x3f3f3f3f;
int n,k,cnt[MAXN+5];
struct node{
	int fst,lst,len,ans;
	node(){fst=lst=len=ans=0;}
	node(int x){
		len=1;ans=INF;if(x) fst=lst=0;
		else fst=INF,lst=-INF;
	}
	node operator +(const node &rhs){
		node res;
		res.len=len+rhs.len;
		res.fst=min(fst,rhs.fst+len);
		res.lst=max(lst,rhs.lst+len);
		res.ans=min(min(ans,rhs.ans),rhs.fst+len-lst);
		return res;
	}
};
vector<node> s[MAXN+5];
void build(int k,int l,int r){
	s[k].resize(r-l+1);if(l==r) return s[k][0]=node(cnt[l]),void();
	int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
	for(int i=0;i<mid-l+1;i++){
		s[k][i]=s[k<<1][i]+s[k<<1|1][i];
		s[k][i+(mid-l+1)]=s[k<<1|1][i]+s[k<<1][i];
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1,x;i<=n;i++) scanf("%d",&x),cnt[x]++;build(1,0,(1<<k)-1);
	for(int i=0;i<(1<<k);i++) printf("%d%c",s[1][i].ans," \n"[i==(1<<k)-1]);
	return 0;
}