2022CCPC江苏省赛题解ACIJKL

2022CCPC江苏省赛题解ACIJKL

A. PENTA KILL!

题意

称一个人连续杀了五个不同的人为五杀.给定 n    ( 1 ≤ n ≤ 1000 ) n\ \ (1\leq n\leq 1000) n  (1n1000)组击杀记录,判断是否有人五杀,若有,则输出"PENTA KILL!“;否则输出"SAD:(”.

思路

预处理出每个人的击杀记录, O ( n 2 ) O(n^2) O(n2)暴力找是否存在相邻的 5 5 5个不同的击杀对象.

代码 -> 2022CCPC江苏省赛-A(模拟)

const int MAXN = 1005;
int n;
vi records[MAXN];
umap<string, int> mp;

int get(string s) {  // 求s离散化后的结果
	if (mp.count(s)) return mp[s];
	else {
		mp[s] = n;
		return n++;
	}
}

void solve() {
	CaseT{
		string a,b; cin >> a >> b;
		records[get(a)].push_back(get(b));
	}
	
	for (int i = 0; i < n; i++) {
		if (records[i].size() < 5) continue;

		for (int l = 0; l <= records[i].size() - 5; l++) {
			uset<int> s;
			for (int j = l; j < l + 5; j++) s.insert(records[i][j]);
			if (s.size() == 5) {
				cout << "PENTA KILL!";
				return;
			}
		}
	}

	cout << "SAD:(";
}

int main() {
	solve();
}


I. Cutting Suffix

题意

对字符串 s s s,下标从 1 1 1开始.定义 s u f f i x i suffix_i suffixi表示 s s s从第 i i i个字符开始的后缀.定义 w i j w_{ij} wij s u f f i x i suffix_i suffixi s u f f i x j suffix_j suffixj的LCP的长度.将 1 ∼ n 1\sim n 1n的整数分为两非空且互补的集合 T 1 T_1 T1 T 2 T_2 T2,求 ∑ i ∈ T 1 ∑ j ∈ T 2 w i j \displaystyle\sum_{i\in T_1}\sum_{j\in T_2}w_{ij} iT1jT2wij的最小值.

第一行输入一个长度为 n    ( 2 ≤ n ≤ 1 e 5 ) n\ \ (2\leq n\leq 1\mathrm{e}5) n  (2n1e5)的且只包含小写英文字母的字符串 s s s.

思路

若串的所有字符都相同,取 T 1 = { n } , T 2 = { 1 , 2 , ⋯   , n − 1 } T_1=\{n\},T_2=\{1,2,\cdots,n-1\} T1={n},T2={1,2,,n1},则任意两字符串的LCP长度为 1 1 1,故答案为 n − 1 n-1 n1.

若串至少有两种字符,将一种字符的出现位置取为 T 1 T_1 T1,另一种字符的出现位置取为 T 2 T_2 T2,则任意两字符串的LCP长度为 0 0 0,故答案为 0 0 0.

代码 -> 2022CCPC江苏省赛-I(思维)

void solve() {
	string s; cin >> s;
	if (s != string(s.length(), s[0])) cout << 0;
	else cout << s.length() - 1;
}

int main() {
	solve();
}


K. aaaaaaaaaaA heH heH nuN

题意

称前缀"nunhehheh"后接若干个(至少 1 1 1个)'a’构成的字符串是优雅的.

t    ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t  (1t1000)组测试数据.每组测试数据第一行输入一个整数 n    ( 0 ≤ n ≤ 1 e 9 ) n\ \ (0\leq n\leq 1\mathrm{e}9) n  (0n1e9).

对每组测试数据,输出一个长度不超过 1 e 6 1\mathrm{e}6 1e6的字符串,使得它恰有 n n n个优雅的子串.若有多组解,输出任一组.数据保证有解.

思路

注意到"nunhehheh"后接 x x x个’a’构成的字符串对答案的贡献为 2 x − 1 2^x-1 2x1,易联想到先用 2 x 2^x 2x凑出 n n n的二进制表示,再用其他字母 + 1 +1 +1.答案从"nunhehhe"开始,即去掉前缀的最后一个’h’,在答案串后接若干个’a’,用’h’控制其后面有几个’a’是有效的.从高到低枚举 n n n的二进制数位,先在答案后接上’a’,若 n n n该数位为 1 1 1,则答案后接上’h’,并 c n t + + cnt++ cnt++.注意 n n n的第 0 0 0位无需接’a’,但若该位为 1 1 1,需更新 c n t cnt cnt,此时凑完了 2 x 2^x 2x的部分.在答案后接上 c n t cnt cnt个’h’,最后接一个’a’即凑完 + 1 +1 +1的部分.

代码 -> 2022CCPC江苏省赛-K(构造+二进制)

void solve() {
	int n; cin >> n;
	
	string ans = "nunhehhe";
	int cnt = 0;  // 要补的'h'的个数
	for (int i = 31; i >= 1; i--) {
		ans.push_back('a');
		if (n >> i & 1) {
			ans.push_back('h');
			cnt++;
		}
	}
	if (n & 1) cnt++;  // 最后一位后不用补'a'

	while (cnt--) ans.push_back('h');
	ans.push_back('a');
	cout << ans << endl;
}

int main() {
	CaseT  // 单测时注释掉该行
	solve();
}


C. Jump and Treasure

题意 ( 2   s 2\ \mathrm{s} 2 s)

x x x轴上有编号 0 ∼ n 0\sim n 0n ( n + 1 ) (n+1) (n+1)个柱子,玩家从第 0 0 0号柱子出发,终点为 [ n + 1 , + ∞ ) [n+1,+\infty) [n+1,+)的平台,每次只能跳到柱子上,且跳跃距离不超过 p p p.除了 0 0 0号柱子,其他柱子上有价值为 a i a_i ai(可为负数)的物品,玩家到达对应的柱子后必须取走.游戏有 n n n个等级,其中第 i i i个等级中玩家只能跳到编号是 i i i的倍数的柱子上.若玩家能在等级 x x x的游戏中到达终点,求其能获得的物品价值之和的最大值.

第一行输入三个整数 n , q , p    ( 2 ≤ p ≤ n ≤ 1 e 6 , 1 ≤ q ≤ 1 e 6 ) n,q,p\ \ (2\leq p\leq n\leq 1\mathrm{e}6,1\leq q\leq 1\mathrm{e}6) n,q,p  (2pn1e6,1q1e6),分别表示柱子数、询问数、最大跳跃距离.第二行输入 n n n个整数 a 1 , ⋯   , a n    ( ∣ a i ∣ ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (|a_i|\leq 1\mathrm{e}9) a1,,an  (ai1e9).接下来 q q q行每行输入一个整数 x    ( 1 ≤ x ≤ n ) x\ \ (1\leq x\leq n) x  (1xn),表示询问若玩家能在等级 x x x的游戏中到达终点,其能获得的物品价值之和的最大值.

对每组询问,若玩家能到达终点,输出其能获得的物品价值之和的最大值;否则输出"Noob".

思路

先考察第 1 1 1个等级. d p [ i ] dp[i] dp[i]表示到达第 i i i个柱子时的最大收益,则显然有 d p [ i ] = max ⁡ i − j ≤ p d p [ j ] + a [ i ] \displaystyle dp[i]=\max_{i-j\leq p}dp[j]+a[i] dp[i]=ijpmaxdp[j]+a[i].暴力转移时间复杂度 O ( n 2 ) O(n^2) O(n2),会TLE,显然可用单调队列优化至 O ( n ) O(n) O(n).

对第 k k k个等级,能跳的柱子的编号为 0 , k , 2 k , ⋯ 0,k,2k,\cdots 0,k,2k,,共 ( ⌊ n k ⌋ + 1 ) \left(\left\lfloor\dfrac{n}{k}\right\rfloor+1\right) (kn+1)个位置,则所有等级中能跳的柱子的数量为 ∑ k = 1 n ( n k + 1 ) \displaystyle\sum_{k=1}^n \left(\dfrac{n}{k}+1\right) k=1n(kn+1),时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码 -> 2022CCPC江苏省赛-C(单调队列优化DP)

const int MAXN = 1e6 + 5;
int n, q, p;  // 柱子数、询问数、最大跳跃距离
int a[MAXN];
ll dp[MAXN];  // dp[i]表示到达第i个柱子时的最大收益

void solve() {
	cin >> n >> q >> p;
	for (int i = 1; i <= n; i++) cin >> a[i];
	while (q--) {
		int x; cin >> x;

		if (x > p) {
			cout << "Noob" << endl;
			continue;
		}

		vi pos;  // 能跳的柱子的编号
		for (int i = 0; i <= n; i += x) pos.push_back(i);
		pos.push_back(n + 1);

		deque<int> que;
		que.push_back(0);
		dp[0] = 0;
		for (auto i : pos) {
			while (que.size() && i - que.front() > p) que.pop_front();  // 超出最大跳跃距离
			dp[i] = dp[que.front()] + a[i];
			
			// 维护单调队列
			while (que.size() && dp[que.back()] < dp[i]) que.pop_back();
			que.push_back(i);
		}
		
		cout << dp[n + 1] << endl;
	}
}

int main() {
	solve();
}


J. Balanced Tree

题意 ( 1.5   s , 64   M B 1.5\ \mathrm{s},64\ \mathrm{MB} 1.5 s,64 MB)

定义二叉树 T T T是好的,如果它是空的或同时满足下列三个条件:① T T T的左子树是好的;② T T T的右子树是好的;③左右子树的节点数之差不超过 1 1 1.求包含 n n n个节点的好二叉树的个数,答案对 2 64 2^{64} 264取模.

t    ( 1 ≤ t ≤ 1 e 6 ) t\ \ (1\leq t\leq 1\mathrm{e}6) t  (1t1e6)组测试数据.每组测试数据输入一个整数 n    ( 0 ≤ n < 2 64 ) n\ \ (0\leq n<2^{64}) n  (0n<264).

思路

f [ n ] f[n] f[n]表示包含 n n n个节点的好二叉树的个数,状态转移方程 f [ n ] = { f [ n − 1 2 ] 2 , n 为奇数 2 f [ n 2 ] ⋅ f [ n 2 − 1 ] , n 为偶数 f[n]=\begin{cases}f\left[\dfrac{n-1}{2}\right]^2,n为奇数 \\ 2f\left[\dfrac{n}{2}\right]\cdot f\left[\dfrac{n}{2}-1\right],n为偶数\end{cases} f[n]= f[2n1]2,n为奇数2f[2n]f[2n1],n为偶数,初始条件 f [ 0 ] = 1 f[0]=1 f[0]=1.

显然答案是 2 2 2的幂次,令 g [ n ] = log ⁡ 2 f [ n ] g[n]=\log_2 f[n] g[n]=log2f[n],则状态转移方程 g [ n ] = { 2 g [ n − 1 2 ] , n 为奇数 g [ n 2 ] + g [ n 2 − 1 ] + 1 , n 为偶数 g[n]=\begin{cases}2g\left[\dfrac{n-1}{2}\right],n为奇数 \\ g\left[\dfrac{n}{2}\right]+g\left[\dfrac{n}{2}-1\right]+1,n为偶数\end{cases} g[n]= 2g[2n1],n为奇数g[2n]+g[2n1]+1,n为偶数,初始条件 g [ 0 ] = 0 g[0]=0 g[0]=0.

显然状态只有 O ( log ⁡ n ) O(\log n) O(logn)个,但时间和空间限制不允许记搜.

考虑优化,设 g [ n ] = a ⋅ g [ x ] + b ⋅ g [ x − 1 ] + c g[n]=a\cdot g[x]+b\cdot g[x-1]+c g[n]=ag[x]+bg[x1]+c

= { a ( 2 g [ x − 1 2 ] ) + b ( g [ x − 1 2 ] + g [ x − 1 2 − 1 ] + 1 ) + c , x 为奇数 a ( g [ x 2 ] + g [ x 2 − 1 ] + 1 ) + b ( 2 g [ x − 1 − 1 2 ] + c ) , x 为偶数 =\begin{cases}a\left(2g\left[\dfrac{x-1}{2}\right]\right)+b\left(g\left[\dfrac{x-1}{2}\right]+g\left[\dfrac{x-1}{2}-1\right]+1\right)+c,x为奇数 \\ a\left(g\left[\dfrac{x}{2}\right]+g\left[\dfrac{x}{2}-1\right]+1\right)+b\left(2g\left[\dfrac{x-1-1}{2}\right]+c\right),x为偶数\end{cases} = a(2g[2x1])+b(g[2x1]+g[2x11]+1)+c,x为奇数a(g[2x]+g[2x1]+1)+b(2g[2x11]+c),x为偶数

= { ( 2 a + b ) g [ x − 1 2 ] + b ⋅ g [ x − 1 2 − 1 ] + c + b , x 为奇数 a ⋅ g [ x 2 ] + ( a + 2 b ) g [ x 2 − 1 ] + c + a , x 为偶数 =\begin{cases}(2a+b)g\left[\dfrac{x-1}{2}\right]+b\cdot g\left[\dfrac{x-1}{2}-1\right]+c+b,x为奇数 \\ a\cdot g\left[\dfrac{x}{2}\right]+(a+2b)g\left[\dfrac{x}{2}-1\right]+c+a,x为偶数\end{cases} = (2a+b)g[2x1]+bg[2x11]+c+b,x为奇数ag[2x]+(a+2b)g[2x1]+c+a,x为偶数.

初始条件 g [ n ] = 1 ⋅ g [ n ] + 0 ⋅ g [ n − 1 ] + 0 g[n]=1\cdot g[n]+0\cdot g[n-1]+0 g[n]=1g[n]+0g[n1]+0,每次 x / = 2 x/=2 x/=2,按奇偶更新 a , b , c a,b,c a,b,c,最后 x = 1 x=1 x=1 g [ n ] = c g[n]=c g[n]=c,即 a n s = 2 c   m o d   2 64 ans=2^c\ \mathrm{mod}\ 2^{64} ans=2c mod 264,故 c ≥ 64 c\geq 64 c64时输出 0 0 0,否则输出 2 c 2^c 2c即可.

代码 -> 2022CCPC江苏省赛-J(优化递推)

ull cal(ull x, ull a, ull b, ull c) {
	if (!x) return 0;
	else if (x == 1) return c;
	else if (x & 1) return cal(x >> 1, a * 2 + b, b, b + c);
	else return cal(x >> 1, a, a + 2 * b, a + c);
}

void solve() {
	ull n; cin >> n;
	
	ull ans = cal(n, 1, 0, 0);
	if (ans >= 64) cout << 0 << endl;
	else cout << ((ull)1 << ans) << endl;
}

int main() {
	CaseT  // 单测时注释掉该行
	solve();
}


L. Collecting Diamonds

题意

给定一长度为 n    ( 1 ≤ s ≤ 2 e 5 ) n\ \ (1\leq s\leq 2\mathrm{e}5) n  (1s2e5)的字符串 s s s,下标从 1 1 1开始.现有操作:选择一个下标 i ∈ [ 1 , n − 2 ]   s . t .   s i = A , s i + 1 = B , s i + 2 = C i\in[1,n-2]\ s.t.\ s_i=A,s_{i+1}=B,s_{i+2}=C i[1,n2] s.t. si=A,si+1=B,si+2=C,若 i i i是奇数,则删除 s i s_i si s i + 2 s_{i+2} si+2;否则删除 s i + 1 s_{i+1} si+1.将剩下的字符串拼起来,并更新 n n n为新的字符串长度.求最大操作次数.

思路

注意到若删除’B’,则其附近的’A’和’C’无法再删除,且该操作会改变在其后面的字符下标的奇偶性;若删除’A’和’C’,若两端还有’A’和’C’,则会与中间剩下的’B’拼起来,可作为下一次的操作对象.显然应在删除’B’前删除尽量多的’A’和’C’.因一个’B’与一对’A’和’C’对应,记录当前删除的’B’的个数 B c n t Bcnt Bcnt即可求出操作次数.

为防止时间复杂度变为 O ( n 2 ) O(n^2) O(n2),实现时不进行操作后的字符串拼接和重新赋下标的操作,而通过下标的奇偶性和之前是否删除过’B’(即改变后面元素的奇偶性)来判断本次操作删除的是’A’和’C’还是’B’,显然这样并不影响操作次数.

代码 -> 2022CCPC江苏省赛-L(贪心)

const int MAXN = 2e5 + 5;
char s[MAXN];

void solve() {
	cin >> s + 1;

	int n = strlen(s + 1);
	int Bcnt = 0;  // 记录当前删了多少个'B'
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		if (s[i] == 'B') {
			int ACcnt = 1;  // 记录最多可删多少个'A'和'C',作为半径故从1开始
			while (i - ACcnt >= 1 && i + ACcnt <= n && s[i - ACcnt] == 'A' && s[i + ACcnt] == 'C') ACcnt++;
			if (--ACcnt == 0) continue;  // 附近不是"ABC",注意减掉一开始多的1

			if (i - 1 & 1) {  // 注意是看下标(i-1)的奇偶性
				ACcnt--;  // 若之前未删过'B',则本次操作删除'A'和'C'
				ans++;  // 此次删除可能是删除'A'和'C'或下面的删除'B',取决于之前是否删过'B'
				
				if (!ACcnt) {  // 'A'和'C'删完了
					if(Bcnt) Bcnt++;  // 若之前删过'B',则本次操作删除'B'

					continue;
				}
			}

			// (i-1)是偶数
			Bcnt++;  // 还有剩下的"ABC",还能再删一个'B'
			ans += min(ACcnt, Bcnt);  // 一个'B'与一对'A'和'C'对应
		}
	}
	cout << ans;
}

int main() {
	solve();
}


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值