CFR-109解题报告

A. Hometask

题意:一个字符串,给定 kk 个限制字符对 (ai,bi)(a_i,b_i),要求从原串中删除尽可能少的字符,使得不存在一个相邻的限制字符对。保证 aibia_i\ne b_i,且每个字符最多只出现在一个字符对中。

做法 1

可以设 f[i][c]f[i][c] 表示前 ii 位,最后一位为 cc 时最少的删除数量,则 j,f[i][j]f[i1][j]+1\forall j,f[i][j]\leftarrow f[i-1][j]+1,而特殊地 f[i][s[i]]min(s[i],j) is available{f[i1][j]}f[i][s[i]]\leftarrow\min\limits_{(s[i],j)\text{ is available}}\{f[i-1][j]\}。时间复杂度为 26n26n

By rng_58

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>

using namespace std;

#define REP(i,n) for((i)=0;(i)<(int)(n);(i)++)
#define foreach(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)

#define INF (1<<29)

string s,t;
bool bad[30][30];
int dp[100010][30];

int main(void){
int K,i,j;

cin >> s;
cin >> K;
REP(i,K){
cin >> t;
bad[t[0]-'a'][t[1]-'a'] = true;
bad[t[1]-'a'][t[0]-'a'] = true;
}

int N = s.length();
REP(i,N+1) REP(j,27) dp[i][j] = -INF;
dp[0][26] = 0;

REP(i,N) REP(j,27){
dp[i+1][j] = max(dp[i+1][j],dp[i][j]);
int x = s[i] - 'a';
if(!bad[j][x]) dp[i+1][x] = max(dp[i+1][x],dp[i][j]+1);
}

int ans = 0;
REP(i,27) ans = max(ans,dp[N][i]);
ans = N - ans;
cout << ans << endl;

return 0;
}

做法 2

由于每个字符最多只出现在一个字符对中,所以字符对之间没有交集、互不影响。我们可以对每一个字符对单独处理一遍:假设当前字符对为 (a,b)(\texttt{a},\texttt{b}),则对于类似 aaababbaa\texttt{aaababbaa} 的连续子段,要么只能保留 a\texttt{a},要么只能保留 b\texttt{b},每次贪心地删掉较少的即可。时间复杂度 O(kn)O(kn)

By peter50216

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<stdio.h>
#include<string.h>
char in[101000];
int ot[300];
char tmp[10];
int main(){
scanf("%s",in);
int k;
scanf("%d",&k);
while(k--){
scanf("%s",tmp);
ot[tmp[0]]=tmp[1];
ot[tmp[1]]=tmp[0];
}
int i,j=0;
int ans=0;
for(i=0;in[i];i=j){
if(ot[in[i]]==0){
j=i+1;
continue;
}
int c1=0,c2=0;
while(in[j]&&(in[j]==in[i]||in[j]==ot[in[i]])){
if(in[j]==in[i])c1++;
else c2++;
j++;
}
if(c1>c2)c1=c2;
ans+=c1;
}
printf("%d\n",ans);
}

B. Colliders

题意:你需要维护一个集合,初始为空。有两种操作:

  1. 尝试加入一个元素。如果集合中没有该元素,且集合中每个元素都与其互质,则成功加入,否则加入失败并输出任意一个不与其互质的元素。
  2. 尝试删除一个元素。如果集合中本来没有该元素则删除失败,否则删除该元素。

可以维护每个质数是否在集合中出现并维护包含该质数的元素。显然每个质数任意时刻只会被一个元素包含,否则会加入失败。每次加入和删除都质因数分解一次即可。

By rng_58

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>

using namespace std;

#define REP(i,n) for((i)=0;(i)<(int)(n);(i)++)
#define foreach(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)

int minp[100010];
bool activated[100010];
int a[100010];

int check(int x){
while(x > 1){
int p = minp[x];
while(x%p == 0) x /= p;
if(a[p] != 0) return a[p];
}
return -1;
}

void activate(int x){
int init = x;
activated[x] = true;
while(x > 1){
int p = minp[x];
while(x%p == 0) x /= p;
a[p] = init;
}
}

void deactivate(int x){
activated[x] = false;
while(x > 1){
int p = minp[x];
while(x%p == 0) x /= p;
a[p] = 0;
}
}

void plus_query(int x){ // cout << "+" << x << endl;
if(activated[x]){
printf("Already on\n");
return;
}

int ans = check(x);
if(ans == -1){
printf("Success\n");
activate(x);
} else {
printf("Conflict with %d\n",ans);
}
}

void minus_query(int x){ // cout << "-" << x << endl;
if(!activated[x]){
printf("Already off\n");
return;
}

printf("Success\n");
deactivate(x);
}

char buf[10];

int main(void){
int N,Q,i,j,x;

scanf("%d%d",&N,&Q);

for(i=2;i<=N;i++) minp[i] = i;
for(i=2;i<=N;i++) if(minp[i] == i) for(j=2*i;j<=N;j+=i) minp[j] = min(minp[j],i);

REP(i,Q){
scanf("%s",buf);
scanf("%d",&x);
if(buf[0] == '+') plus_query(x); else minus_query(x);
}

return 0;
}

C.