Harbour.Space Scholarship Contest 2021-2022 题解

多好的上分机会啊,要是换个时间(指改在 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;
}
上一篇:整除(数论)分块


下一篇:关于∑n div i的求法