CFR-109解题报告
A. Hometask
题意:一个字符串,给定 \(k\) 个限制字符对 \((a_i,b_i)\),要求从原串中删除尽可能少的字符,使得不存在一个相邻的限制字符对。保证 \(a_i\ne b_i\),且每个字符最多只出现在一个字符对中。
做法 1
可以设 \(f[i][c]\) 表示前 \(i\) 位,最后一位为 \(c\) 时最少的删除数量,则 \(\forall j,f[i][j]\leftarrow f[i-1][j]+1\),而特殊地 \(f[i][s[i]]\leftarrow\min\limits_{(s[i],j)\text{ is available}}\{f[i-1][j]\}\)。时间复杂度为 \(26n\)。
By rng_58
#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
由于每个字符最多只出现在一个字符对中,所以字符对之间没有交集、互不影响。我们可以对每一个字符对单独处理一遍:假设当前字符对为 \((\texttt{a},\texttt{b})\),则对于类似 \(\texttt{aaababbaa}\) 的连续子段,要么只能保留 \(\texttt{a}\),要么只能保留 \(\texttt{b}\),每次贪心地删掉较少的即可。时间复杂度 \(O(kn)\)。
By peter50216
#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
题意:你需要维护一个集合,初始为空。有两种操作:
- 尝试加入一个元素。如果集合中没有该元素,且集合中每个元素都与其互质,则成功加入,否则加入失败并输出任意一个不与其互质的元素。
- 尝试删除一个元素。如果集合中本来没有该元素则删除失败,否则删除该元素。
可以维护每个质数是否在集合中出现并维护包含该质数的元素。显然每个质数任意时刻只会被一个元素包含,否则会加入失败。每次加入和删除都质因数分解一次即可。
By rng_58
#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;
}